《设计模式之禅》读书笔记(二)
23种设计模式
单例模式
定义:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
实现:Singleton类称为单例类,通过使用private的构造函数确保了在一个应用中只产生一个实例,并且是自行实例化的(在Singleton中自己使用new Singleton())。
扩展:有上限的多例模式。
工厂方法模式
定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
实现:Creator为抽象创建类,也就是抽象工厂,具体如何创建产品类是由具体的实现工厂ConcreteCreator完成的。可以利用模板成员函数创建具体产品类ConcreteProduct。
说明:工厂方法模式是new一个对象的替代品,所以在所有需要生成对象的地方都可以使用,但是需要慎重地考虑是否要增加一个工厂类进行管理,增加代码的复杂度。
扩展:简单工厂模式(一个模块仅需要一个工厂类,没有必要把它产生出来,使用静态的方法就可以)。
抽象工厂模式
定义:为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类。
说明:抽象工厂类AbstractCreator的职责是定义每个工厂要实现的功能,即不同产品族的产品创建。有N个产品族,在抽象工厂类中就应该有N个创建方法。有M个产品等级就应该有M个实现工厂类,在每个实现工厂中,实现不同产品族的生产任务。
实践:抽象工厂模式的使用场景定义非常简单:一个对象族(或是一组没有任何关系的对象)都有相同的约束,则可以使用抽象工厂模式。例如一个文本编辑器和一个图片处理器,都是软件实体,但是*nix下的文本编辑器和Windows下的文本编辑器虽然功能和界面都相同,但是代码实现是不同的,图片处理器也有类似情况。也就是具有了共同的约束条件:操作系统类型。于是我们可以使用抽象工厂模式,产生不同操作系统下的编辑器和图片处理器。
模板方法模式
定义:定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
实现:AbstractClass叫做抽象模板,它的方法分为两类:基本方法也叫做基本操作,是由子类实现的方法,并且在模板方法被调用。模板方法可以有一个或几个,一般是一个具体方法,也就是一个框架,实现对基本方法的调度,完成固定的逻辑。
说明:抽象模板中的基本方法尽量设计为protected类型,符合迪米特法则,不需要暴露的属性或方法尽量不要设置为protected类型。实现类若非必要,尽量不要扩大父类中的访问权限。为了防止恶意的操作,一般模板方法都加上final关键字,不允许被覆写。
建造者模式
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
实现:建造者中需要一些配置参数和方法,以产生不同的产品。
说明:相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模式。建造者模式最主要的功能是基本方法的调用顺序安排,也就是这些基本方法已经实现了,通俗地说就是零件的装配,顺序不同产生的对象也不同;而工厂方法则重点是创建,创建零件是它的主要职责,组装顺序则不是它关心的。
代理模式
定义:为其他对象提供一种代理以控制对这个对象的访问。
扩展:普通代理、强制代理(强制代理的概念就是要从真实角色查找到代理角色,不允许直接访问真实角色)、动态代理(动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象)。
原型模式
定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
实现:不通过new关键字来产生一个对象,而是通过对象复制来实现的模式就叫做原型模式。原型模式先产生出一个包含大量共有信息的类,然后可以拷贝出副本,修正细节信息,建立了一个完整的个性对象。
中介者模式
定义:用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
实现:
Mediator 抽象中介者角色。抽象中介者角色定义统一的接口,用于各同事角色之间的通信。
Concrete Mediator 具体中介者角色。具体中介者角色通过协调各同事角色实现协作行为,因此它必须依赖于各个同事角色。
Colleague 同事角色。每一个同事角色都知道中介者角色,而且与其他的同事角色通信的时候,一定要通过中介者角色协作。每个同事类的行为分为两种:一种是同事本身的行为,比如改变对象本身的状态,处理自己的行为等,这种行为叫做自发行为(Self-Method),与其他的同事类或中介者没有任何的依赖;第二种是必须依赖中介者才能完成的行为,叫做依赖方法(Dep-Method)。
命令模式
定义:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
实现:调用者角色与接收者角色之间没有任何依赖关系,调用者实现功能时只需调用Command抽象类的execute方法就可以,不需要了解到底是哪个接收者执行。
责任链模式
定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
实现:责任链模式的重点是在“链”上,由一条链去处理相似的请求。在链中决定谁来处理这个请求,并返回相应的结果。抽象的处理者实现三个职责:一是定义一个请求的处理方法handleMessage,唯一对外开放的方法;二是定义一个链的编排方法setNext,设置下一个处理者;三是定义了具体的请求者必须实现的两个方法:定义自己能够处理的级别getHandlerLevel和具体的处理任务echo。
说明:链中节点数量需要控制,避免出现超长链的情况,一般的做法是在Handler中设置一个最大节点数量,在setNext方法中判断是否已经是超过其阈值,超过则不允许该链建立,避免无意识地破坏系统性能。
装饰器
定义:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。
实现:
Component抽象构件。Component是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象。注意 在装饰模式中,必然有一个最基本、最核心、最原始的接口或抽象类充当Component抽象构件。
ConcreteComponent。具体构件ConcreteComponent是最核心、最原始、最基本的接口或抽象类的实现,要装饰的就是它。
Decorator。装饰角色一般是一个抽象类,实现接口或者抽象方法,在它的属性里必然有一个private变量指向Component抽象构件。
具体装饰角色。ConcreteDecoratorA和ConcreteDecoratorB是两个具体的装饰类,要把你最核心的、最原始的、最基本的东西装饰成其他东西。
说明:抽象的装饰器Decorator其实是Component构件的一个代理。在转发请求的过程中调用需要添加的功能。
策略模式
定义:定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。
实现:
Context封装角色。也叫做上下文角色,起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。
Strategy抽象策略角色。策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。
ConcreteStrategy具体策略角色。实现抽象策略中的操作,该类含有具体的算法。
适配器模式
定义:将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
实现:
Target目标角色。该角色定义把其他类转换为何种接口,也就是我们的期望接口,。。
Adaptee源角色。想把谁转换成目标角色,这个“谁”就是源角色,它是已经存在的、运行良好的类或对象,经过适配器角色的包装,它会成为一个崭新、靓丽的角色。
Adapter适配器角色。适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责非常简单:通过继承或是类关联的方式把源角色转换为目标角色。
说明:类适配器通过继承实现;对象适配器通过聚合实现。
迭代器
定义:它提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。
组合模式
定义:将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
实现:
Component抽象构件角色。定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性。
Leaf叶子构件。叶子对象,其下再也没有其他的分支,也就是遍历的最小单位。
Composite树枝构件。树枝对象,它的作用是组合树枝节点和叶子节点形成一个树形结构。
说明:只要是树形结构,就要考虑使用组合模式。只要是要体现局部和整体的关系的时候,而且这种关系还可能比较深,考虑一下组合模式。
观察者模式
定义:观察者模式(Observer Pattern)也叫做发布订阅模式(Publish/subscribe)。定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
实现:Observable是被观察者,在Observable接口中有三个比较重要的方法,分别是addObserver增加观察者,deleteObserver删除观察者,notifyObservers通知所有的观察者。管理观察者并通知观察者。
说明:和责任链模式的最大区别就是观察者广播链在传播的过程中消息是随时更改的,它是由相邻的两个节点协商的消息结构;而责任链模式在消息传递过程中基本上保持消息不可变,如果要改变,也只是在原有的消息上进行修正。
门面模式(外观模式)
定义:要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。
实现:
Facade门面角色。客户端可以调用这个角色的方法。此角色知晓子系统的所有功能和责任。一般情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去,也就说该角色没有实际的业务逻辑,只是一个委托类。
subsystem子系统角色。可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集合。子系统并不知道门面的存在。对于子系统而言,门面仅仅是另外一个客户端而已。
说明:
为一个复杂的模块或子系统提供一个供外界访问的接口。
子系统相对独立——外界对子系统的访问只要黑箱操作即可。
备忘录模式
定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
实现:
Originator发起人角色。记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。
Memento备忘录角色。负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。
Caretaker备忘录管理员角色。对备忘录进行管理、保存和提供备忘录。
说明:
使用Clone方式的备忘录模式,可以使用在比较简单的场景或者比较单一的场景中,尽量不要与其他的对象产生严重的耦合关系。
多状态的备忘录模式,利用HashMap对多状态进行映射。
多备份的备忘录,增加检查点(Check Point),也就是在备份的时候做的戳记,系统级的备份一般是时间戳。
访问者模式
定义:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
实现:
Visitor抽象访问者。抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法的参数定义哪些对象是可以被访问的。
ConcreteVisitor具体访问者。它影响访问者访问到一个类后该怎么干,要做什么事情。
Element抽象元素接口或者抽象类。声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。
ConcreteElement具体元素。实现accept方法,通常是visitor.visit(this),基本上都形成了一种模式了。
ObjectStruture结构对象元素产生者。一般容纳在多个不同类、不同接口的容器,如List、Set、Map等,在项目中,一般很少抽象出这个角色。
说明:
业务规则要求遍历多个不同的对象。这本身也是访问者模式出发点,迭代器模式只能访问同类或同接口的数据,而访问者模式是对迭代器模式的扩充,可以遍历不同的对象,然后执行不同的操作,也就是针对访问的对象不同,执行不同的操作。
单分派语言处理一个操作是根据请求者的名称和接收到的参数决定的;双分派意味着得到执行的操作决定于请求的种类和两个接收者的类型,它是多分派的一个特例。
状态模式
定义:当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。
实现:
State抽象状态角色接口或抽象类。负责对象状态定义,并且封装环境角色以实现状态切换。
ConcreteState具体状态角色。每一个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理,通俗地说,就是本状态下要做的事情,以及本状态如何过渡到其他状态。
Context环境角色。定义客户端需要的接口,并且负责具体状态的切换。
说明:状态模式的核心是封装,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样。把状态对象声明为静态常量,有几个状态对象就声明几个静态常量。环境角色具有状态抽象角色定义的所有行为,具体执行使用委托方式。
解释器模式
定义:给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
享元模式
定义:使用共享对象可有效地支持大量的细粒度的对象。享元模式(Flyweight Pattern)是池技术的重要实现方式。
内部状态。内部状态是对象可共享出来的信息,存储在享元对象内部并且不会随环境改变而改变,它们可以作为一个对象的动态附加信息,不必直接储存在具体某个对象中,属于可以共享的部分。
外部状态。外部状态是对象得以依赖的一个标记,是随环境改变而改变的、不可以共享的状态,它是一批对象的统一标识,是唯一的一个索引值。
实现:
Flyweight抽象享元角色。它简单地说就是一个产品的抽象类,同时定义出对象的外部状态和内部状态的接口或实现。
ConcreteFlyweight具体享元角色。具体的一个产品类,实现抽象角色定义的业务。该角色中需要注意的是内部状态处理应该与环境无关,不应该出现一个操作改变了内部状态,同时修改了外部状态,这是绝对不允许的。
unsharedConcreteFlyweight不可共享的享元角色。不存在外部状态或者安全要求(如线程安全)不能够使用共享技术的对象,该对象一般不会出现在享元工厂中。
FlyweightFactory享元工厂。职责非常简单,就是构造一个池容器,同时提供从池中获得对象的方法。
说明:在对象池中,对象一旦产生,必然有一个唯一的、可访问的状态标志该对象,而且池中的对象声明周期是由池容器决定,而不是由使用者决定的。
桥梁模式
定义:将抽象和实现解耦,使得两者可以独立地变化。
实现:
Abstraction抽象化角色。它的主要职责是定义出该角色的行为,同时保存一个对实现化角色的引用,该角色一般是抽象类。
Implementor实现化角色。它是接口或者抽象类,定义角色必需的行为和属性。
RefinedAbstraction修正抽象化角色。它引用实现化角色对抽象化角色进行修正。
ConcreteImplementor具体实现化角色。它实现接口或抽象类定义的方法和属性。
抽象角色引用实现角色,或者说抽象角色的部分实现是由实现角色完成的。