PythonDesignPatterns

前言

学习Python设计模式。

设计模式是什么

设计模式是软件设计中常见问题的典型解决方案。 它们就像能根据需求进行调整的预制蓝图, 可用于解决代码中反复出现的设计问题。

设计模式与方法或库的使用方式不同, 你很难直接在自己的程序中套用某个设计模式。 模式并不是一段特定的代码, 而是解决特定问题的一般性概念。 你可以根据模式来实现符合自己程序实际所需的解决方案。

人们常常会混淆模式和算法, 因为两者在概念上都是已知特定问题的典型解决方案。 但算法总是明确定义达成特定目标所需的一系列步骤, 而模式则是对解决方案的更高层次描述。 同一模式在两个不同程序中的实现代码可能会不一样。

算法更像是菜谱: 提供达成目标的明确步骤。 而模式更像是蓝图: 你可以看到最终的结果和模式的功能, 但需要自己确定实现步骤。

设计模式的优势

  • 设计模式是针对软件设计中常见问题的工具箱, 其中的工具就是各种经过实践验证的解决方案。 即使你从未遇到过这些问题, 了解模式仍然非常有用, 因为它能指导你如何使用面向对象的设计原则来解决各种问题。
  • 设计模式定义了一种让你和团队成员能够更高效沟通的通用语言。 你只需说 “哦, 这里用单例就可以了”, 所有人都会理解这条建议背后的想法。 只要知晓模式及其名称, 你就无需解释什么是单例。

设计模式的分类

不同设计模式的复杂程度、 细节层次以及在整个系统中的应用范围等方面各不相同。 我喜欢将其类比于道路的建造: 如果你希望让十字路口更加安全, 那么可以安装一些交通信号灯, 或者修建包含行人地下通道在内的多层互通式立交桥。

最基础的、 底层的模式通常被称为惯用技巧。 这类模式一般只能在一种编程语言中使用。

最通用的、 高层的模式是构架模式。 开发者可以在任何编程语言中使用这类模式。 与其他模式不同, 它们可用于整个应用程序的架构设计。

此外, 所有模式可以根据其意图或目的来分类。以下分为三类:

  • 创建型模式:提供创建对象的机制, 增加已有代码的灵活性和可复用性。
  • 结构性模式:介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。
  • 行为模式:负责对象间的高效沟通和职责委派。

创建型模式

工厂方法模式

介绍

工厂方法模式是一种创建型设计模式,其在父类中提供一个创建对象的方法,允许子类决定实例化对象的类型。

问题

假设你正在开发一款物流管理应用。最初版本只能处理卡车运输, 因此大部分代码都在位于名为卡车的类中。一段时间后, 这款应用变得极受欢迎。客户希望应用能够支持海上物流功能。然而目前大部分代码都与卡车类相关,在程序中添加轮船类需要修改全部代码,更糟糕的是, 如果你以后需要在程序中支持另外一种运输方式, 很可能需要再次对这些代码进行大幅修改。此时我们应该如何处理这种问题呢?

方案

工厂方法模式建议使用特殊的工厂方法代替对于对象构造函数的直接调用 (即使用new运算符)。对象仍将通过new运算符创建, 只是该运算符改在工厂方法中调用罢了。 工厂方法返回的对象通常被称作 “产品”。

乍看之下,这种更改可能毫无意义:我们只是改变了程序中调用构造函数的位置而已。但是,仔细想一下,现在你可以在子类中重写工厂方法,从而改变其创建产品的类型。但有一点需要注意:仅当这些产品具有共同的基类或者接口时,子类才能返回不同类型的产品,同时基类中的工厂方法还应将其返回类型声明为这一共有接口。

举例来说,​卡车Truck和轮船Ship类都必须实现运输Transport接口,该接口声明了一个名为deliver交付的方法。 每个类都将以不同的方式实现该方法:卡车走陆路交付货物,轮船走海路交付货物。 ​陆路运输Road­Logistics类中的工厂方法返回卡车对象,而海路运输Sea­Logistics类则返回轮船对象。

调用工厂方法的代码(通常被称为客户端代码)无需了解不同子类返回实际对象之间的差别。客户端将所有产品视为抽象的运输。客户端知道所有运输对象都提供交付方法,但是并不关心其具体实现方式。

示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
from __future__ import annotations
from abc import ABC, abstractmethod


class Creator(ABC):
"""
Creator类声明了工厂方法,该方法应该返回Product类的对象。创建者的子类通常提供该方法的实现。
"""
@abstractmethod
def factory_method(self):
"""
请注意,Creator类还可能提供工厂方法。
"""
pass

def some_operation(self) -> str:
"""
还要注意,Creator类的主要责任不是在创建产品。通常,它包含一些核心业务逻辑依赖于factory方法
返回的Product对象。子类可以通过重写工厂方法,并从中返回不同类型的产品。
"""
# 调用工厂方法创建一个Product对象
product = self.factory_method()

# 使用product.
result = f"Creator: The same creator's code has just worked with {product.operation()}"

return result


"""
Concrete Creators覆盖factory方法以更改生成的产品类型。
"""


class ConcreteCreator1(Creator):
"""
注意,该方法的签名仍然使用抽象产品类型,即使具体的产品实际上是从该方法返回的。创建者可以保持独立于具体产品类的方式。
"""

def factory_method(self) -> Product:
return ConcreteProduct1()


class ConcreteCreator2(Creator):
def factory_method(self) -> Product:
return ConcreteProduct2()


class Product(ABC):
"""
Product接口声明所有具体产品必须实现。
"""

@abstractmethod
def operation(self) -> str:
pass


"""
Concrete Products提供了产品界面的各种实现。
"""


class ConcreteProduct1(Product):
def operation(self) -> str:
return "{Result of the ConcreteProduct1}"


class ConcreteProduct2(Product):
def operation(self) -> str:
return "{Result of the ConcreteProduct2}"


def client_code(creator: Creator) -> None:
"""
客户端代码与具体创建者的实例一起工作,尽管是通过其基本接口。只要客户端通过基本接口,可以将其传递给任何创建者的子类。
"""

print(f"Client: I'm not aware of the creator's class, but it still works.\n"
f"{creator.some_operation()}", end="")


if __name__ == "__main__":
print("App: Launched with the ConcreteCreator1.")
client_code(ConcreteCreator1())
print("\n")

print("App: Launched with the ConcreteCreator2.")
client_code(ConcreteCreator2())

抽象工厂模式

介绍:抽象工厂模式是一种创建型设计模式,它能创建一系列相关的对象,而无需指定其具体类。

生成器模式

介绍:声称其模式是一种创建型设计模式,使你能够分步骤创建复杂对象,该模式允许你使用相同的创建代码生成不同类型和形式的对象。

原型模式

介绍:原型模式是一种创建型设计模式,使你能够复制已有对象,而又无需使代码依赖它们所属的类。

单例模式

介绍:单例模式是一种创建型设计模式, 让你能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点。

结构性模式

适配器模式

介绍:适配器模式是一种结构型设计模式, 它能使接口不兼容的对象能够相互合作。

桥接模式

介绍:桥接模式是一种结构型设计模式, 可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构, 从而能在开发时分别使用。

组合模式

介绍:组合模式是一种结构型设计模式, 你可以使用它将对象组合成树状结构, 并且能像使用独立对象一样使用它们。

装饰模式

介绍:装饰模式是一种结构型设计模式, 允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。

外观模式

介绍:外观模式是一种结构型设计模式, 能为程序库、 框架或其他复杂类提供一个简单的接口。

享元模式

介绍:享元模式是一种结构型设计模式, 它摒弃了在每个对象中保存所有数据的方式, 通过共享多个对象所共有的相同状态, 让你能在有限的内存容量中载入更多对象。

代理模式

介绍:代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。

行为模式

责任链模式

介绍:责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。

命令模式

介绍:命令模式是一种行为设计模式, 它可将请求转换为一个包含与请求相关的所有信息的独立对象。 该转换让你能根据不同的请求将方法参数化、 延迟请求执行或将其放入队列中, 且能实现可撤销操作。

迭代器模式

介绍:迭代器模式是一种行为设计模式, 让你能在不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素。

中介者模式

介绍:中介者模式是一种行为设计模式, 能让你减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互, 迫使它们通过一个中介者对象进行合作。

备忘录模式

介绍:备忘录模式是一种行为设计模式, 允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态。

观察者模式

介绍:观察者模式是一种行为设计模式, 允许你定义一种订阅机制, 可在对象事件发生时通知多个 “观察” 该对象的其他对象。

状态模式

介绍:状态模式是一种行为设计模式, 让你能在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。

策略模式

介绍:策略模式是一种行为设计模式, 它能让你定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换。

模板方法模式

介绍:模板方法模式是一种行为设计模式, 它在超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。

访问者模式

介绍:访问者模式是一种行为设计模式, 它能将算法与其所作用的对象隔离开来。