设计模式Head First

https://book.douban.com/subject/2243615/


OO基础:

  1. 抽象
  2. 封装
  3. 多态
  4. 继承

OO原则:

  1. 封装变化
  2. 多用组合,少用继承
  3. 针对接口而非实现编程
  4. 为交互对象之间的松耦合设计而努力
  5. 对扩展开放,对修改关闭(OCP)
  6. 依赖抽象,不依赖具体类
  7. 只和朋友交谈/最少知识原则(减少暴露的接口,减少耦合)
  8. 别找我,我会找你(好莱坞原则)

模式是在某个情境(context)下,针对某问题的某种解决方案。

一个设计模式,应该由下面几个部分组成:

  1. 名称(Name): 如果没有好的名称,该模式就无法成为你和其他开发人员之间共享词汇的一部分
  2. 意图(Intent): 该模式的作用,或者是该模式的定义
  3. 动机(Motivation): 问题以及解决这个问题的具体场景
  4. 适用性(Applicability): 该模式可以被应用在什么场合
  5. 结构(Strucuture): 参与此模式的类之间的关系图示
  6. 参与者(Participants): 此设计中所涉及到的类和对象在模式中的责任和角色
  7. 协作(Collaborations): 告诉我们参与者如何在此模式中协作
  8. 结果(Consequence): 采用此模式之后可能产生的效果,好与不好的
  9. 实现和示例代码(Implementation/Sample Code): 在实现中的技巧以及可能遇到的问题
  10. 已知应用(Known Uses): 用于描述已经在真实系统中发现的模式例子
  11. 相关模式(Related Patterns): 和其他模式之间的关系

Pasted-Image-20231225103709.png

设计模式也存在缺点。设计模式通常产生一些额外的类和对象,所以会增加设计的复杂度。设计模式也会在你的设计中增加更多的层,这不但增加复杂性,而且效率下降。


共享模式词汇的威力

共享词汇的五种方式:

  1. 设计会议中:当你和你的团队在会议中讨论软件设计时,使用设计模式可以帮你们待在“设计”中久一点。从设计模式和面向对象原则的视角讨论设计,可以避免你的团队很快陷入实现的细节,也可以避免发生许多误解。
  2. 和其他开发人员:当你和其他开发人员讨论的时候,可以使用模式,这可以帮助其他开发人员学习新模式并建立一个社群。和别人分享你所学会的东西是很有成就感的一件事情。
  3. 在架构文档中:在你当你在编写架构文档的时候,使用模式将会缩减文档的篇幅,并且让读者更清晰的了解你的设计。
  4. 在代码注释以及命名习惯上:当你在编写代码的时候,应该在注释中清楚地注明你所使用的模式。在选择类和方法的名称时,应尽可能地显示出隐藏在下面的模式。其他的开发人员在阅读你的代码时会感激你,因为你让他们能够很快的了解你的实现。
  5. 将志同道合的开发人员结合在一起:分享你的知识。许多开发人员都听过模式,但并不真正了解什么是模式。你可以自愿给他们讲一堂模式介绍,或者成立一个读书会。

OO模式:

  1. 策略模式(Strategy):定义算法族,暴露相同的接口,分别封装起来,让它们之间可以相互替换使用。此模式让算法的变化独立于使用算法的客户。通常实现是定义一个Behaviour接口,这个接口里面只有一个run函数,然后不同具体算法实现这个Behaviour接口。
  2. 观察者模式(Observer):在对象之间定义一对多的依赖,当某个对象改变状态时,所以依赖它的对象都会受到通知更新。JDK中的java.util.Observable接口是一种通用的观察者模式实现。
  3. 装饰器模式(Decorator): 动态地将责任附加到对象上。想要扩展功能,装饰者提供有别于继承的另外一种选择。装饰器模式和代理模式有点相似,差别在于装饰器需要在原有实现上做扩展,而代理通常是用于中转请求。
  4. 抽象工厂模式(Abstract Factory): 提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。在抽象工厂内部,普遍使用了工厂方法模式。抽象工厂模式相比工厂方法,更强调里面各种create接口创建出来的对象之间的组合关系。
  5. 工厂方法模式(Factory Method): 定义一个创建对象的接口,但是由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类中。通常父类内部有个createX的抽象方法,返回某个抽象类X,而在子类中去实现createX这个方法,返回抽象类X的子类。
  6. 单件模式(Singleton): 确保一个类只有一个实例,并提供全局访问点。
  7. 命令模式(Command): 将请求封装成为对象,里面包含了完成这次请求的相关对象和上下文。通常这个对象有execute和undo两个方法,用于执行命令和撤销命令。
  8. 适配器模式(Adapter): 将一个类的接口,转换成为客户期望的另外一个接口。适配器让原本不兼容的类可以相互协作。
  9. 外观接口(Facade): 将子系统中多个接口的复杂调用,包装成为一个接口让外部使用。这样可以减少外部依赖性,并且更加易于使用。想象一个如果我们使用咖啡机做一杯卡布,“将加热水,将咖啡豆研磨,制作好咖啡,加热牛奶,打起奶泡,倒入咖啡”这几个步骤由一个按钮完成,是不是非常方便?
  10. 模板方法模式(Template Method): 在父类的一个方法中定义算法骨架,将其中一些步骤推迟到子类中完成。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。模板方法和策略有点类似,策略更强调算法之间可以相互替换,可以有很多策略而且策略通常都比较轻巧(只需要实现接口),而模板方法则强调算法骨架代码和其中可变代码的配合。JDK中最典型的例子就是实现Comparable接口来达到排序功能。
  11. 迭代器模式(Iterator): 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
  12. 组合模式(Composite): 允许将对象组成属性结构来表现“整体/部分”的层次结构。组合能让客户以一致的方式处理个别对象和对象组合。
  13. 状态模式(State): 允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
  14. 代理模式(Proxy): 为另外一个对象提供一个替身或占位符以访问这个对象。

Pasted-Image-20231225105107.png

下图是java.io.InputStream的类层次结构,FilterInputStream在实现上很明显使用了装饰器风格。在Java语言里面,装饰器通常需要继承有待装饰的类,然后在需要装饰的方法上增加自己的实现。

Pasted-Image-20231225104859.png


书中提到的其余的几个设计模式:

  1. 桥接模式(Bridge): 将实现和抽象放在两个不同的类层次中而使用它。
  2. 生成器模式(Builder): 将一个复杂对象的创建过程封装起来。相比工场模式,生成器模式产生对象单一,但是产生过程复杂。
  3. 责任链模式(Chain of Responsibility)
  4. 蝇量(Flyweight): 让某一个实例来提供多个虚拟实例。减少创建对象带来的开销,缺点是没有办法对某个虚拟实例做修改和扩展。
  5. 解释器(Interpreter): DSL
  6. 中介者模式(Mediator): 将相关对象之间的复杂的沟通和控制集中到一个类里面,缺点是中介者本身逻辑会特别复杂。
  7. 备忘录模式(Memento): 允许对对象状态进行序列化和反序列化
  8. 原型(Prototype): Javascript, Lua metatable
  9. 访问者模式(Visitor): 当遍历某个负责的数据结构时,由数据结构本身提供访问导游,在适当的节点上调用访问者的访问函数。比如XML的SAX, 访问者需要提供各种节点的handler.