核心目标:管理变化,提高复用

初学者有一个误区:把设计模式当成算法去学习,会去分析它的结构,理解代码的调用流程。这些在初学的时候是有必要的,但是这样是远远不够的。

学习设计模式时,核心目标要牢牢掌握:管理变化,提高复用!

你的设计方案是否达到了提高复用性的设计目标

  • 误用了模式,你引入了一个设计模式,但是后来发现在这个场景下没有引入的必要性,并没有增加复用性。这样的话,就违背了引入设计模式的初衷:提高复用。

两种手段:分解vs.抽象

分解

  • 在面向对象思想中,你首先是要思考如何分解
  • 这和人认识世界的规律是一样的,小孩子刚开始认识这个世界的时候:这是梨,这是苹果,这是香蕉
  • 建立一种分解的思维,知道它们有所不同

抽象

  • 在分解之后,要建立一种抽象思维
  • 一个小孩在过段时间他知道,这一类都归属于水果这个类型
  • 在程序中,抽象出相同的行为与类,就可以建立统一的处理。水果具有哪些共同特征,交通工具都有哪些共同特征与行为。

所以,软件设计跟人认识世界的模型是非常类似的。

八大原则

这八大原则,比某一个具体的模式更重要。要将它们内化成一种认知,一种理解能力。

要对八大原则很熟,比如

  1. 策略模式,你一看这枚举类型,就发现它违背了开闭原则
  2. 桥模式,违背单一职责原则
  3. 除了单例、享元是处理性能问题的,其他设计模式都和八大原则有很大关系。

八大原则往往是相辅相成的,某一程度上是相通的
比如

  • 你违背了依赖倒置原则,你往往就违背了开闭原则
  • 你违背了单一职责原则,通常依赖倒置原则也违背了

重构技法

这五种技法也是一样,是相辅相成,在某种程度上是相通的。

静态->动态

静态绑定的东西要转成动态绑定

早绑定->晚绑定

继承->组合

编译时依赖->运行时依赖

紧耦合->松耦合

从封装变化角度对模式分类

未被业界认可,是李建忠老师在教学、项目经验中总结出来的,觉得这种分类方式能够帮助大家更容易掌握设计模式。
image.png

有一些模式在当下比较不常用

  • Builder
  • Mediator
  • Memento:被序列化的方案替代
  • Iterator:C++泛型编程所发展出来的编译时多态,以模板的方式支持更灵活的迭代器
  • Chain Of Resposibility:目前直接用某些数据结构就可以去表达了
  • Command:在C++中,被仿函数替代
  • Visitor:前提条件很苛刻,用的话也很重,会提出很庞大的类层次结构
  • Interpreter:小而简单的文法可以用,更复杂的文法,用面向对象的方式也解决不了,管理也复杂,性能也下降,通常使用一些开源的工具来做

在C++对象模型的层面看设计模式

在C++中有三种结构

  1. A继承B的情况:在A的对象模型中,B的成员是在最前面的
  2. A组合一个B对象:这种情况和第一种情况的对象模型是一模一样的,B也是在A成员变量的前面
  3. A组合一个B指针:指针具有多态性,B指针可以指向它的子类,从而带来灵活性

image.png

如果把GoF所有设计模式的类名换成A、B等这种字母,你会发现几乎所有的模式都是图中的第三种结构

  • 所有设计模式最后都是疏通同归,都是通过指针指向一个多态对象,从而实现灵活性

关注变化点和稳定点

  • 如果你假设软件的所有地方都是变化的,那没有一个设计模式可以帮助到你
  • 如果你假设软件的所有地方都是稳定的,那你完全没有必要用设计模式

因为设计模式的宗旨是:管理变化,提高复用。
因此,你应该区分清楚,哪些地方是稳定点,哪些地方是变化点,然后在变化点的地方应用合适的设计模式,从而达到管理变化,提高复用性的核心目的。

什么时候不用模式?

  • 代码可读性很差时
    • 变量命名、类层次结构很乱、文件划分、逻辑划分都很乱。那就没有必要用设计模式了,你先把代码的可读性理清楚。可以参考《重构》
  • 需求理解还很浅时
    • 对需求很懵懂的时候,先不用设计模式,把第一版本写出来。后面需求慢慢明确了,再用
  • 变化没有显现时
    • 变化没有显现时,你可能会预测变化,这时候会导致过度的使用设计模式
  • 不是系统的关键依赖点
    • 不是系统的关键模块,就没有必要用设计模式,比如一些外包型的公司,就不会去用。但是,如果是做产品级的软件,就会用设计模式,并且这会在将来的工作中受益
  • 项目没有复用价值时
  • 项目将要发布时
    • 都要上线了,你不能引入设计模式,而引出了一些BUG

经验之谈

  1. 不要为模式而模式
  2. 关注抽象类和接口
  3. 理清变化点和稳定点
  4. 审视依赖关系
  5. 要有Framework和Application的区隔思维
  6. 良好的设计是演化的结果

设计模式成长的路径

“手中无剑,心中无剑”:见模式而不知

  • 见到设计模式,你都不知道它是设计模式

“手中有剑,心中无剑”:可以识别模式,作为应用开发人员使用模式

  • 会照猫画虎,能够从代码中识别出模式,并且知道怎么使用这些设计模式

“手中有剑,心中有剑”:作为框架开发人员为应用设计某些模式

  • 你已经深入理解设计模式,你能知道怎么表达,你可以做一个框架开发人员,让别人来使用你的模式

“手中无剑,心中有剑”:忘掉模式,只有原则

  • 已经忘记了有哪些设计模式,只记得了原则,根据这些原则,可以研究出自己的设计模式
  • 代码只要符合某个设计原则,能够解决耦合性问题,得到复用性的提高。这就是一种设计模式