Head First设计模式(第二版)

本文最后更新于:2023年9月17日 下午

1 设计模式介绍

设计原则:

  • 识别应用中变化的方面,把它们和不变的方面分开

    • 把会变化的部分取出并封装,这样,以后你就可以修改或扩展这个部分,而不会影响其他不需要变化的部分。
  • 针对接口编程,而不是针对实现编程。

    • “针对接口编程”真正的意思是“针对超类型编程”。
  • 优先使用组合而不是继承

策略模式(Strategy):策略模式定义了一个算法族,分别封装起来,使得它们之间可以互相变换。策略让算法的变化独立于使用它的客户。

2 观察者模式

观察者模式(Observer):定义对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

设计原则:尽量做到交互的对象之间的松耦合设计。

3 装饰者模式

开放-关闭原则:类应该对扩展开放,但对修改关闭。

装饰者模式(Decorator):动态地将额外责任附加到对象上。用于扩展功能,装饰者提供子类变化之外的弹性替代方案。

4 工厂模式

工厂模式(Factory Method):定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法让类把实例化推迟到子类。

依赖倒置原则(Dependency Inversion Principle):依赖抽象,不依赖具体类。

抽象工厂模式(Abstract Factory):提供一个接口来创建相关或依赖对象的家族,而不需要制定具体类。

5 单件模式

单件模式(Singleton):确保一个类只有一个实例,并提供一个全局访问点。

go语言的一个单件模式的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package singleton

import (
"sync"
)

// Singleton 类型定义
type Singleton struct {
// ... 其他字段
}

var instance *Singleton
var once sync.Once

// GetInstance 用于获取单一实例
func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{}
})
return instance
}

6 命令模式

命令模式(Command):把请求封装为对象,以便用不同的请求、队列或者日志请求来参数化其他对象,并支持可撤销的操作。

7 适配器和外观模式

适配器模式(Adapter):将一个类的接口转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作。

外观模式(Facade):为子系统中的一组接口提供了一个统一的接口。外观定义了一个更高级别的接口,使得子系统更容易使用。

最少知识原则:只和你的密友谈话。

  • 当你在设计一个系统时,对于任何对象,都要注意它所交互的类的数量,以及它和这些类的交互。
  • 这个原则防止我们创建有大量的类在一起的设计,免得系统一部分的变化会连锁影响到其他部分。当你在许多类之间造成许多依赖时,你的系统就是一个易碎的系统,需要花费许多成本维护,而且复杂得难以让别人理解。

8 模版方法模式

模版方法模式(Template Method):在一个方法中定义一个算法的骨架,而把一些步骤延迟到子类。模版方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

好莱坞原则:不要打电话给(调用)我们,我们会打电话给(调用)你。

9 迭代器和组合模式

迭代器模式(Iterator Pattern):提供一种方式,可以访问一个聚合对象中的元素而又不暴露其潜在的表示。

单一责任原则:一个类应该只有一个变化的原因。

  • 类的每个责任都是一个潜在的变化区域。超过一个责任,意味着超过一个变化区域。
  • 这个原则知道我们要让每个类保持单一责任。

组合模式(Composite Pattern):允许你将对象组合成树形结构来表现部分-整体层次结构。组合让客户可以统一处理个别对象和对象组合。

10 状态模式

状态模式(State):允许对象在内部状态改变时改变其行为。对象看起来好像改变了它的类。

11 代理模式

代理模式(Proxy):为另一个对象提供一个替身或占位符来控制对这个对象的访问。

12 复合模式

复合模式(Composite):复合模式把两个或更多的模式结合成一个解决方案,解决重复发生或一般的问题。

13 更好地与设计模式相处

模式:模式是在某个上下文中针对某个问题的解决方案。

  • 上下文指某个模式适用的情况。这应该是一种会不断出现的情况。
  • 问题指在此上下文中你想要达到的目标,但也要考虑该上下文中发生的任何约束。
  • 解决方案就是你所追求的东西:一个通用的设计,所有人都可以用来解决目标和约束集。

根据模式目标分类

创见型模式

创建型模式牵涉到对象实例化,这类模式都提供一种将客户从需要实例化的对象中解耦的方式。

单件、抽象工厂、工厂方法、生成器、原型

行为型模式

行为型模式都牵涉到类和对象如何交互,以及分配责任。

模版方法、命令、迭代器、观察者、策略、状态、责任链、解释器、中介者、备忘录、访问者

结构型模式

结构型模式让你组合类或对象得到更大的结构。

装饰者、代理、外观、组合、适配器、桥接、蝇量

根据模式处理的是类还是对象分类

类模式

类模式描述类之间的关系如何通过继承定义。类模式中的关系是在编译时建立的。

模版方法、工厂方法、适配器、解释器

对象模式

对象模式描述对象之间的关系,而且对象模式主要通过组合定义。对象模式中的关系通常在运行时创建,更加动态和有弹性。

组合、装饰者、代理、策略、外观、命令、迭代器、观察者、抽象工厂、单件、状态、桥接、中介者、责任链、访问者、备忘录、原型、蝇量

反模式

反模式告诉你如何从问题到达一个坏的解决方案。

14 剩下的模式

桥接(Bridge)

使用桥接模式不知改变你的实现,也改变你的抽象。

优缺点:

优点 用途和缺点
解耦实现,不再永久绑定到接口 在跨多个平台的图形和窗口系统中很有用。
抽象和实现可以独立扩展 当你需要以不同的方式改变接口和实现时,桥接很好用。
针对“具体的抽象类”的变化,不会影响客户 缺点是增加了复杂度。

生成器(Builder)

使用生成器模式来封装一个产品的构造过程,并允许按步骤构造。

优缺点:

优点 用途和缺点
封装复杂对象的构造方式 经常被用来建造组合结构
允许对象用多个步骤构造,并且可以改变过程(和一步到位的工厂不同) 和使用工厂相比,构造对象需要更多客户的领域知识。
向客户隐藏产品的内部表现
产品的实现可以被替换,因为客户只看到抽象的接口

责任链(Chain)

当你要把一个处理请求的机会给予多余一个的对象时,使用责任链模式。

优缺点:

优点 用途和缺点
解耦请求的发送者和接收者 经常用在窗体系统中,处理鼠标点击和键盘事件。
简化你的对象,因为它不需要知道链的结构以及保持对其成员的直接引用 并不保证请求一定会被执行;如果没有对象处理它,可能会掉到链的末尾(这可以是优点或缺点)
允许你通过改变链的成员或次序,动态地添加或移除责任。 运行时可能会难以观察和调试

蝇量(Flyweight)

当某个类的一个实例可以用于提供许多虚拟实例时,使用蝇量模式。

优缺点:

优点 用途和缺点
减少运行时对象实例的树木,节省内存 当一个类有许多实例,而这些实例能够用一致的方法控制时,用蝇量。
把许多“虚拟”对象的状态集中放进一个地方 蝇量模式的一个缺点在于,一旦你实现了它,那么类的单个逻辑实例,将无法拥有和其他实例不同的独立行为。

解释器(Interpreter)

使用解释器模式为语言建造解释器。

优缺点:

优点 用途和缺点
将每一个语法规则表达成一个类,使得语言容易实现 当你需要实现一门简单的语言时,使用解释器
因为语法由类表达,你可以轻易地改变或扩展该语言 当你有一个单间的语法,而且简单比效率重要时,适合用解释器
通过在类结构中添加方法,可以添加解释之外的新行为,例如打印格式的美化和更复杂的程序验证。 用于脚本和编程语言
当语法规则的数目很大时,这个模式可能变得笨重。在这些情况下,一个解析器/编译器的生成器可能更适合。

中介者(Mediator)

使用中介者模式来集中相关对象之间复杂的沟通和控制方式。

优缺点:

优点 用途和缺点
通过将中介者支持的对象从系统中解耦,增加对象的复用性。 中介者常用于协调相关的GUI组件
通过将控制逻辑集中,简化了系统的维护。 中介者模式的一个缺点是,如果设计不当,中介者对象本身会变得过度复杂。
简化以减少系统中对象之间发送的消息的变化。

备忘录(Memento)

当你需要让对象返回之前的某个状态时,例如,你的用户请求“撤销”,使用备忘录模式。

优缺点:

优点 用途和缺点
保持被保存的状态处于关键对象外面,有助于维护内聚 备忘录用于保持状态
保持关键对象的数据封装 使用备忘录的一个缺点是,保存和恢复状态可能相当耗时
提供容易实现的恢复能力 在Java系统中,考虑使用序列化(Serialization)来保存系统的状态。

原型(Prototype)

当创建给定类的实例昂贵或者复杂时,使用原型模式。

优缺点:

优点 用途和缺点
向客户隐藏制作新实例的复杂性 在一个复杂的类层次中,当系统必须创建许多类型的新对象时,应该考虑原型。
提供一种选择,让客户生成类型未知的对象 使用原型的缺点是,对象的复制有时候相当复杂。
在某些环境下,复制对象比创建新对象更有效

访问者(Visitor)

当你想要为一个对象组合增加能力,且封装不重要时,使用访问者模式。

优缺点:

优点 用途和缺点
允许你添加操作到组合结构,而不改变结构本身 使用访问者模式时,组合类的封装被打破
添加新操作相对容易 因为涉及到导游的功能,因此组合结构更加难以变化。
由访问者执行的操作的代码被集中化了

附录

  1. The Porland Patterns Repository
  2. Hillside Group
  3. O’Reilly Online Learning

本文作者: Uyouii
文章链接: https://uyouii.cool/posts/31212927/
版权声明: 本博客文章除特别声明外, 均采用署名4.0国际(CC BY 4.0)国际许可协议进行授权, 转载请注明出处


Head First设计模式(第二版)
https://uyouii.cool/posts/31212927/
作者
Uyouii
发布于
2023年9月17日
许可协议