创建型

单例模式

让系统中的对象拥有唯一的实例,一般用于配制项或者基础组件的加载,具有线程安全的特性。

多例模式

工厂模式

可以按照规则批量生产具有某些特征的对象,便于使用。

结构型

组合模式(Composite Pattern)

定义

也叫合成模式,主要用来描述部分-整体的关系。

Compose objects into tree structures to represent part-whole hierarchies.Composite lets clients treat individual objects and compositions of objects uniformly.

将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

组合模式
组合模式

组成

  • Component抽象构件角色
    定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性,比如我们例子中的getInfo就封装到了抽象类中。
  • Leaf叶子构件
    叶子对象,其下再也没有其他的分支,也就是遍历的最小单位。
  • Composite树枝构件
    树枝对象,它的作用是组合树枝节点和叶子节点形成一个树形结构。

优缺点

优点

  • 高层次模块儿调用简单
    一棵树形机构中的所有节点都是Component,局部和整体对调用者来说没有任何区别,也就是说,高层模块不必关心自己处理的是单个对象还是整个组合结构,简化了高层模块的代码。
  • 节点自由增加
    使用了组合模式后,我们可以看看,如果想增加一个树枝节点、树叶节点是不是都很容易,只要找到它的父节点就成,非常容易扩展,符合开闭原则,对以后的维护非常有利。

缺点

在场景类中的定义,树叶和树枝直接使用了实现类!这在面向接口编程上是很不恰当的,与依赖倒置原则冲突,读者在使用的时候要考虑清楚,它限制了你接口的影响范围。

使用场景

  • 维护和展示部分-整体关系的场景,如树形菜单、文件和文件夹管理。
  • 从一个整体中能够独立出部分模块或功能的场景。

注意事项

只要是树形结构,就要考虑使用组合模式,这个一定要记住,只要是要体现局部和整体的关系的时候,而且这种关系还可能比较深,考虑一下组合模式吧。

应用

行为型

桥接模式(Bridge Pattern)

也叫做桥梁模式,其定义如下:
Decouple an abstraction from its implementation so that the two can vary independently.
将抽象和实现解耦,使得两者可以独立地变化。

桥接模式
桥接模式

组成

  • Abstraction——抽象化角色
    它的主要职责是定义出该角色的行为,同时保存一个对实现化角色的引用,该角色一般是抽象类。
  • Implementor——实现化角色
    它是接口或者抽象类,定义角色必需的行为和属性。
  • RefinedAbstraction——修正抽象化角色
    它引用实现化角色对抽象化角色进行修正。
  • ConcreteImplementor——具体实现化角色
    它实现接口或抽象类定义的方法和属性

优点

  • 抽象和实现分离
    这也是桥梁模式的主要特点,它完全是为了解决继承的缺点而提出的设计模式。在该模式下,实现可以不受抽象的约束,不用再绑定在一个固定的抽象层次上。
  • 优秀的扩充能力
    看看我们的例子,想增加实现?没问题!想增加抽象,也没有问题!只要对外暴露的接口层允许这样的变化,我们已经把变化的可能性减到最小。
  • 实现细节对客户透明
    客户不用关心细节的实现,它已经由抽象层通过聚合关系完成了封装。

缺点

  • 不希望或不适用使用继承的场景
    例如继承层次过渡、无法更细化设计颗粒等场景,需要考虑使用桥梁模式。
  • 接口或抽象类不稳定的场景
    明知道接口不稳定还想通过实现或继承来实现业务需求,那是得不偿失的,也是比较失败的做法。
  • 重用性要求较高的场景
    设计的颗粒度越细,则被重用的可能性就越大,而采用继承则受父类的限制,不可能出现太细的颗粒度。

注意事项

桥梁模式是非常简单的,使用该模式时主要考虑如何拆分抽象和实现,并不是一涉及继承就要考虑使用该模式,那还要继承干什么呢?桥梁模式的意图还是对变化的封装,尽量把可能变化的因素封装到最细、最小的逻辑单元中,避免风险扩散。因此读者在进行系统设计时,发现类的继承有N层时,可以考虑使用桥梁模式。

应用

备忘录模式

目的

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

Without violating encapsulation,capture and externalize an object's internal state so that the object can be restored to this state later.

备忘录模式
备忘录模式

组成

  • Originator发起人角色
    记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。
  • Memento备忘录角色
    负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。
  • Caretaker备忘录管理员角色
    对备忘录进行管理、保存和提供备忘录。

优缺点

优点
  • 高层次模块儿调用简单
    一棵树形机构中的所有节点都是Component,局部和整体对调用者来说没有任何区别,也就是说,高层模块不必关心自己处理的是单个对象还是整个组合结构,简化了高层模块的代码。
  • 节点自由增加
    使用了组合模式后,我们可以看看,如果想增加一个树枝节点、树叶节点是不是都很容易,只要找到它的父节点就成,非常容易扩展,符合开闭原则,对以后的维护非常有利。
缺点

在场景类中的定义,树叶和树枝直接使用了实现类!这在面向接口编程上是很不恰当的,与依赖倒置原则冲突,读者在使用的时候要考虑清楚,它限制了你接口的影响范围。

应用

使用场景
  • 保存和恢复数据的相关状态场景。
  • 供一个可回滚(rollback)的操作;比如Word中的CTRL+Z组合键,IE浏览器中的后退按钮,文件管理器上的backspace键等。
  • 要监控的副本场景中。例如要监控一个对象的属性,但是监控又不应该作为系统的主业务来调用,它只是边缘应用,即使出现监控不准、错误报警也影响不大,因此一般的做法是备份一个主线程中的对象,然后由分析程序来分析。
  • 库连接的事务管理就是用的备忘录模式,想想看,如果你要实现一个JDBC驱动,你怎么来实现事务?还不是用备忘录模式嘛
注意事项
  • 备忘录的生命期
    备忘录创建出来就要在“最近”的代码中使用,要主动管理它的生命周期,建立就要使用,不使用就要立刻删除其引用,等待垃圾回收器对它的回收处理。
  • 备忘录的性能
    不要在频繁建立备份的场景中使用备忘录模式(比如一个for循环中),原因有二:一是控制不了备忘录建立的对象数量;二是大对象的建立是要消耗资源的,系统的性能需要考虑。因此,如果出现这样的代码,设计师就应该好好想想怎么修改架构了。

参考文献

  • 秦小波.《设计模式之蝉(第2版)》. 机械工业出版社, 2014.
  • 杨春晖.《系统架构设计师教程》. 清华大学出版社, 2012.

相关文章

Q.E.D.