虽然没有任何两个编程项目是相同的,但在一个项目过程中你通常会有一种似曾相识的感觉。这种在某个地方见过同样问题的感觉事实上是你认识到在很多相似的架构问题中可以应用相同的方法。这些方法称为设计模式(design pattern)
1.1 什么是设计模式
设计模式在面向对象编程中用于区分并解决场景的设计问题。与框架和类库不同,设计模式是抽象的,提供了如何解决某一类编程问题的建议,但不提供完整的输出代码来实现这些建议。
设计模式在二十世纪九十年代早期变得流行起来,并由《设计模式:可复用面向对象软件的基础》这本书的出版将其形式化。但是这些想法出现在这本书之前。许多核心的设计模式,比如,迭代器(Iterator)、单例(Singleton),被大多数JAVA和C++程序员熟知并得到广泛的使用。其他模式,比如建造者模式(Builder),使用的情况较少,但在某些情况下非常有用
1.2 为什么使用设计模式
有两个原因使得设计模式非常有用。
一个明显的原因是它可以基于大量程序员的智慧,帮助解决常见的软件设计问题。这使得它们成为非常宝贵的教育工具一级编程资源。第二个原因--可能是最重要的原因--设计模式为讨论设计问题及其解决方案提供了简明的词汇。
1.3 23中设计模式
Gamma等人写的《设计模式》一书,正式和详细地描述23中基本的设计模式。它将这些设计模式分为3类:
创建型模式、行为型模式、结构型模式
1.3.1 创建型模式
这些模式管理类的选择和对象的创建。将创建对象的代码交给应用程序中的其他部分进行,或者限制对象实例如何被创建和访问是有益处的,而不是直接创建对象。
单例模式
单例模式确保了一个类在给定时间内只有一个实例。这个实例充当一个共享资源的看门人,或者作为一个中央通信枢纽。一个应用程序不能创建新的实例——所有的方法都通过该单一实例访问。应用程序通过这个类对外暴露一个静态方法来调用该实例。
核心的系统功能通常使用单例模式访问。例如,在java中,java.lang.Runtime类就使用单例模式与应用程序的运行环境交互。单例模式有时也被用来替代全局变量,但是这并没有避免困扰全局变量的状态问题,所以许多人认为这样使用是一个反模式的情况。
为什么单例模式优于使用一组静态方法?
❤继承与接口——单例是对象。它们可以从基类继承并实现接口。
❤可能的多样性——你可以改变你的想法,创建多个对象(例如每个线程一个)同时需要修改大量代码。(如果不这样 做,就不在是单例了)
❤动态绑定——真正用来创建单例的类可以不在编译时决定,而在运行时决定。
单例模式并不是没有不足之处。由于在多线程环境中必须对方法调用进行同步,所以减慢了对单例的访问。一个单例可能拖慢应用程序的启动时间,因为 它需要初始化,并且可能会一直持有不再需要的资源。
建造者模式
建造者模式(builder)以逐步的方式创建对象,并且不知道也不关心这些对象是如何构造的。你初始化一个建造者并让他为你构造一个对象,而不是直接构造一个对象(或通过工厂)。
建造者对于需要多个构造函数参数的对象初始化特别有用,特别是在这些参数的类型相同或相似时。
工厂模式
工厂模式将工厂方法——任何主要目的是创建并放回一个新对象的方法——的概念应用于一个类层次结构。一个基类定义了一个工厂方法,该方法可以在子类中重写,由子类决定如何创建一个对象。基类可能提供也可能未提供该方法的默认实现,但总是使用工厂方法来创建一个所需类型的新对象。
工厂模式通常用来实现抽象工厂模式。
抽象工厂模式
工厂是一个可以创建其他对象的对象。抽象工厂模式将工厂的实现与使用该工厂的代码分开。
抽象工厂的典型实现具有一组从一个抽象基类继承的工厂类。在选择了使用哪个工厂实现后,应用程序只是通过抽象类,而不是具体的实现类来使用它。所以工厂的选择可以推迟到运行的时候——可能是通过一个配置文件来选择——甚至在一个程序运行的时候改变。
如果这些工厂类都是不同的或者只有一个工厂类时,不要使用抽象工厂模式。
抽象工厂模式与工厂模式紧密相关。单例模式通常用来实现抽象工厂模式。
1.3.1 行为型模式
这些模式用于确定类和对象之间如何交互和通信。
迭代器模式
迭代器模式使得你可以遍历一个数据结构中的所有元素,并不需要知道也不需要关心这些元素是如何存储和表示的。许多现代编程语言通常都提供了对迭代器的支持。
迭代器有许多种类,并且各自的使用都有不同的取舍。最简单的迭代器提提供对元素单向遍历,并且不允许对底层的数据结构进行任何修改。更加复杂的迭代器允许双向遍历,并且允许对底层的数据结构增加或删除元素。
观察者模式
观察者模式让对象对状态的改变,对感兴趣的观察者进行广播,而对观察者并不需要知道很多。这种减轻耦合度的做法也称为发布--订阅模式。观察者使用一个通用接口将其自身注册在一个主题(要观察的对象),来接收通知 。该主题会在状态改变时通知到每个注册的观察者。
在许多用户界面中常见的模型-视图-控制器(MVC)职责分离,是一个典型的观察者模式应用的例子,当模型改变时(底层的数据结构),自动导致视频(用户界面)重绘。
需要注意的是,观察者模式并不会指定哪类信息需要被传递给观察者,它们更新状态的顺序,以及改变传播的速度和频率。这些实现细节对于性能以及整个系统的可用性都有很大影响。
结构性模式
这些模式组织类和对象之间的关系,提供了将相关的对象组织在一起来完成一个预期的行为的指导。
装饰模式
装饰模式通过将一个对象“包装”进另一个对象来修改它的行为,并与原对象实现相同的接口。因此装饰模式有时候称作包装模式。
在装饰模式中有四种类:组件、具体组件、装饰器、和具体装饰器、组件是一个抽象类或者接口,定义了具体组件和装饰器共同的接口。装饰器是一个抽象类,包装具体组件(你希望修改的类),并转发所有对组件的方法调用。在具体装饰器中,通过重写一个或多个其父类装饰器类的方法来修改具体组件的方法。重写后的方法通常在调用相应的底层具体组件方法之前或之后,提供一些额外的功能。
装饰模式提供了继承的替代方案。多个不同的具体装饰器可以应用于同一个具体组件实例,每个连续的装饰为对象提供了另一层包装。底层的具体组件的行为被所有包装它的装饰器进行了修改。