python 设计模式 01

  |  

设计模式简介

设计模式(Design pattern)是解决软件开发某些特定问题而提出的一些解决方案也可以理解成解决问题的一些思路。对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。

每一个设计模式系统地命名、解释和评价了面向对象系统中一个重要的和重复出现的设计。

这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。通过设计模式可以帮助我们增强代码的可重用性、可扩充性、 可维护性、灵活性好。

我们使用设计模式最终的目的是实现代码的高内聚和低耦合。

设计模式中有一些重要的人物如,“四人帮”(Gang of Four, GoF):Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides,他们写了一本书在设计模式中影响比较大《设计模式:可复用面向对象软件的基础》

概念

这里的设计模式特指OOP中的设计模式,说明之前需要明确几个概念。

面向对象

面向对象的三大特性:

  • 封装:将数据、方法封装到一个类里;具有公有和私有属性。
  • 继承:多个类之间复用代码使用继承。
  • 多态:python本身就是一门多态语言,不用程序员去考虑多态,语言特性自己做了。

接口

接口:若干抽象方法的集合。
作用:限制实现接口的类必须按照接口给定的调用方式实现这些方法;对高层模块隐藏了类的内部实现。

1
2
3
4
@abstractmethod:抽象方法,含abstractmethod方法的类不能实例化,继承了含abstractmethod方法的子类必须复写所有abstractmethod装饰的方法,未被装饰的可以不重写
@property:方法伪装属性,方法返回值及属性值,被装饰方法不能有参数,必须实例化后调用,类不能调用
@classmethod:类方法,可以通过实例对象和类对象调用,被该函数修饰的方法第一个参数代表类本身常用cls,被修饰函数内可调用类属性,不能调用实例属性
@staticmethod:静态方法,可以通过实例对象和类对象调用,被装饰函数可无参数,被装饰函数内部通过类名.属性引用 类属性或类方法,不能引用实例属性
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
# abstract class 有抽象方法就是抽象类:抽象类不可被实例化
from abc import ABC, abstractmethod


# class BaseAlgorithmInterface:
# def run(self):
# raise NotImplementedError
#
class BaseAlgorithmInterface(ABC):
# 该类被子类继承时必须实现被abstractmethod装饰的方法
@abstractmethod
def run(self):
pass


class Colmap(BaseAlgorithmInterface):
def run(self):
print('do something with Colmap')


class HashNerf(BaseAlgorithmInterface):
def run(self):
print('do something with HashNert')


c = Colmap()
c.run()

面向对象设计SOLID原则

  1. 单一职责原则

    就一个类而言,应该仅有一个引起它变化的原因。

    如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱他的设计,当变化发生时,设计会遭受到意想不到的破坏;软件设计真正要做的许多内容就是发现职责并把那些职责相互分离。

  2. 开放-封闭原则

    一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。

    该原则是面向对象设计的核心所在,遵循这个原则可以带来面向对象技术所声称的可维护、可扩展、可复用、灵活性好。

    设计人员必须对于他设计的模块应该对哪种变化封闭做出选择,必须先猜测出最有可能发生的变化种类,然后构造抽象来隔离那些变化。最初编写程序时假设变化不会发生,当变化发生时,就创建抽象来隔离以后发生的同类变化,拒绝不成熟的抽象。

  3. 里氏替换原则

    所有引用父类的地方必须能透明地使用其子类的对象。

    由于子类型的可替换性才使得使用父类类型的模块在无需修改的情况下就可以扩展。

  4. 依赖倒置原则

    高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。

    要针对接口编程,而不是针对实现编程。该原则可以说是面向对象设计的标志,编写时考虑的是如何对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止于抽象类或者接口。

  5. 迪迷特原则(最少知识原则)

    如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用;如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。

    该原则其根本思想,是强调了类之间的松耦合;类之间的耦合越弱,越利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。在类的结构设计上,每一个类都应当尽量降低成员的访问权限。

  6. 接口隔离原则

    使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。

  7. 合成/聚合复用原则

    尽量使用合成/聚合,尽量不要使用类继承。

    聚合表示一种弱的“拥有”关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;合成则是一种强的“拥有”关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。

    优先使用对象的合成/聚合将有助于你保持每个类被封装,并被击中在单个任务上,这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。

设计模式分类

  1. 创建型模式(5种)

    对象实例化的模式,创建型模式用于解耦对象的实例化过程。

    • 工厂方法模式
    • 抽象工厂模式
    • 创建者模式
    • 原型模式
    • 单例模式
  2. 结构型模式(7种)

    把类或对象结合在一起形成一个更大的结构。

    • 适配器模式
    • 桥模式
    • 组合模式
    • 装饰模式
    • 外观模式
    • 享元模式
    • 代理模式
  3. 行为型模式(11种)

    类和对象如何交互,及划分责任和算法。

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

创建型模式

简单工厂模式

内容

不直接向客户端暴露对象创建的实现细节,而是通过一个工厂类来负责创建产品类的实例。

角色

  • 工厂角色(Creator)
  • 抽象产品角色(Product)
  • 具体产品角色(Concrete Product)

实现

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
from abc import ABC, abstractmethod


class BaseAlgorithmInterface(ABC):
@abstractmethod
def run(self, dataset=None, params=None, output_dir=None):
pass


class Colmap(BaseAlgorithmInterface):
def run(self, dataset=None, params=None, output_dir=None):
print(f'do something(dataset: {dataset}, params: {params}, output_dir: {output_dir}) with Colmap')


class HashNerf(BaseAlgorithmInterface):
def __init__(self, application=None):
self.application = application

def run(self, dataset=None, params=None, output_dir=None):
if self.application:
print(f'do something(dataset: {dataset}, params: {params}, output_dir: {output_dir}) with {self.application} base on HashNerf')
else:
print(f'do something(dataset: {dataset}, params: {params}, output_dir: {output_dir}) with HashNerf')


class AlgorithmFactory:
def create_algorithm(self, name):
match name:
case 'colmap':
return Colmap()
case 'hashnerf':
return HashNerf()
case 'slam':
return HashNerf(application='slam')
case _:
raise ValueError(f'Algorithm {name} is not supported')


# ##### 客户端使用 #####
# 算法应用创建工厂
algorithm_factory = AlgorithmFactory()

nerf = algorithm_factory.create_algorithm('hashnerf')
nerf.run(dataset='dataset', params='params', output_dir='output_dir')

# 隐藏类的内部实现
slam = algorithm_factory.create_algorithm('slam')
slam.run(dataset='dataset', params='params', output_dir='output_dir')

优缺点

优点:

  • 隐藏了对象创建的实现细节
  • 客户端不需要修改代码

缺点:

  • 违反了单一职责原则,将创建逻辑几种到一个工厂类里
  • 当添加新产品时,需要修改工厂类代码,违反了开闭原则

工厂方法模式

内容

定义一个用于创建对象的接口(工厂接口),让子类决定实例化哪一个产品类。

角色

  • 抽象工厂角色(Creator)
  • 具体工厂角色(Concrete Creator)
  • 抽象产品角色(Product)
  • 具体产品角色(Concrete Product)

实现

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
from abc import ABC, abstractmethod


class BaseAlgorithmInterface(ABC):
@abstractmethod
def run(self, dataset=None, params=None, output_dir=None):
pass


class Colmap(BaseAlgorithmInterface):
def run(self, dataset=None, params=None, output_dir=None):
print(f'do something(dataset: {dataset}, params: {params}, output_dir: {output_dir}) with Colmap')


class HashNerf(BaseAlgorithmInterface):
def __init__(self, application=None):
self.application = application

def run(self, dataset=None, params=None, output_dir=None):
if self.application:
print(f'do something(dataset: {dataset}, params: {params}, output_dir: {output_dir}) with {self.application} base on HashNerf')
else:
print(f'do something(dataset: {dataset}, params: {params}, output_dir: {output_dir}) with HashNerf')


# ##### 具体工厂 #####
class ColmapFactory:
def create_algorithm(self):
return Colmap()


class HashNerfFactory:
def create_algorithm(self):
return HashNerf()


class SlamFactory:
def create_algorithm(self):
return HashNerf(application='slam')


# ##### 客户端使用 #####
nerf = HashNerfFactory().create_algorithm()
nerf.run(dataset='dataset', params='params', output_dir='output_dir')

slam = SlamFactory().create_algorithm()
slam.run(dataset='dataset', params='params', output_dir='output_dir')

优缺点

优点:

  • 每个具体产品都对应一个具体工厂类,不需要修改工厂类代码
  • 隐藏了对象创建的实现细节

缺点:

  • 每增加一个具体产品类,就必须增加一个相应的具体工厂类

抽象工厂模式

内容

定义一个工厂类接口,让工厂子类来创建一系列相关或相互依赖的对象。

例:生产一部手机,需要手机壳、CPU、操作系统三类对象进行组装,其中每类对象都有不同的种类。对每个具体工厂,分别生产一部手机所需要的三个对象。

相比工厂方法模式,抽象工厂模式中的每个具体工厂都生产一套产品。

角色

  • 抽象工厂角色(Creator)
  • 具体工厂角色(Concrete Creator)
  • 抽象产品角色(Product)
  • 具体产品角色(Concrete Product)
  • 客户端(Client)

实现

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
from abc import ABC, abstractmethod


# ##### 抽象产品 #####
class PhoneShell(ABC):
@abstractmethod
def show_shell(self):
pass


class CPU(ABC):
@abstractmethod
def show_cpu(self):
pass


class OS(ABC):
@abstractmethod
def show_os(self):
pass


# ##### 抽象工厂 #####
class PhoneFactory(ABC):
@abstractmethod
def make_shell(self):
pass

@abstractmethod
def make_cpu(self):
pass

@abstractmethod
def make_os(self):
pass


# ##### 具体产品 #####
class SmallShell(PhoneShell):
def show_shell(self):
print("普通手机小手机壳")


class BigShell(PhoneShell):
def show_shell(self):
print("普通手机大手机壳")


class AppleShell(PhoneShell):
def show_shell(self):
print("苹果手机壳")


class SnapDragonCPU(CPU):
def show_cpu(self):
print("骁龙CPU")


class MediaTekCPU(CPU):
def show_cpu(self):
print("联发科CPU")


class AppleCPU(CPU):
def show_cpu(self):
print("苹果CPU")


class Android(OS):
def show_os(self):
print("Android系统")


class IOS(OS):
def show_os(self):
print("iOS系统")


# ##### 具体工厂 #####
class MiFactory(PhoneFactory):
def make_cpu(self):
return SnapDragonCPU()

def make_os(self):
return Android()

def make_shell(self):
return BigShell()


class HuaweiFactory(PhoneFactory):
def make_cpu(self):
return MediaTekCPU()

def make_os(self):
return Android()

def make_shell(self):
return SmallShell()


class IPhoneFactory(PhoneFactory):
def make_cpu(self):
return AppleCPU()

def make_os(self):
return IOS()

def make_shell(self):
return AppleShell()


# ##### 客户端使用 #####
class Phone:
def __init__(self, cpu, os, shell):
self.cpu = cpu
self.os = os
self.shell = shell

def show_info(self):
print("手机信息:")
self.cpu.show_cpu()
self.os.show_os()
self.shell.show_shell()


def make_phone(factory):
cpu = factory.make_cpu()
os = factory.make_os()
shell = factory.make_shell()
return Phone(cpu, os, shell)


p1 = make_phone(IPhoneFactory())
p1.show_info()

优缺点

优点:

  • 将客户端与类的具体实现相分离
  • 每个工厂创建了一个完整的产品系列,使得易于交换产品系列
  • 有利于产品的一致性(即产品之间的约束关系)

缺点:

  • 难以支持新种类的(抽象)产品

建造者模式

内容

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

角色

  • 抽象建造者(Builder)
  • 具体建造者(Concrete Builder)
  • 指挥者(Director)
  • 产品(Product)

实现

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
from abc import ABC, abstractmethod


# ##### 产品 #####
class Player:
def __init__(self, face=None, body=None, arm=None, leg=None):
self.face = face
self.body = body
self.arm = arm
self.leg = leg

def __str__(self):
return "%s, %s, %s, %s" % (self.face, self.body, self.arm, self.leg)


# ##### 抽象建造者 #####
class PlayerBuilder(ABC):
@abstractmethod
def build_face(self):
pass

@abstractmethod
def build_body(self):
pass

@abstractmethod
def build_arm(self):
pass

@abstractmethod
def build_leg(self):
pass


# ##### 具体建造者 #####
class SexyGirlBuilder(PlayerBuilder):
def __init__(self):
self.player = Player()

def build_face(self):
self.player.face = "漂亮脸蛋"

def build_body(self):
self.player.body = "苗条"

def build_arm(self):
self.player.arm = "漂亮胳膊"

def build_leg(self):
self.player.leg = "大长腿"


class Monster(PlayerBuilder):
def __init__(self):
self.player = Player()

def build_face(self):
self.player.face = "怪兽脸"

def build_body(self):
self.player.body = "怪兽身材"

def build_arm(self):
self.player.arm = "长毛的胳膊"

def build_leg(self):
self.player.leg = "长毛的腿"


# ##### 指挥者 #####
class PlayerDirector: # 控制组装顺序
def build_player(self, builder):
builder.build_body()
builder.build_face()
builder.build_arm()
builder.build_leg()
return builder.player


# ##### 客户端使用 #####
builder = Monster()
director = PlayerDirector()
player = director.build_player(builder)
print(player)

建造者模式与抽象工厂模式相似,也用来创建复杂对象。

主要区别是建造者模式着重一步步构造一个复杂对象,而抽象工厂模式着重于多个系列的产品对象。

优缺点

优点:

  • 隐藏了一个产品的内部结构和装配过程
  • 将构造代码与表示代码分开,构造顺序修改不影响表示代码
  • 可以对构造过程进行更精细的控制

单例模式

内容

保证一个类只有一个实例,并提供一个访问它的全局访问点。

角色

单例(Singleton)

实现

在 Python 中,我们可以用多种方法来实现单例模式:

  • 使用模块
  • 使用装饰器
  • 使用类
  • 基于 new 方法实现
  • 基于 metaclass 方式实现

1. 使用模块

Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。

因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。

单独定义一个文件,保存为 singleton.py 文件内容如下:

1
2
3
4
5
6
class Singleton:
def run(self):
print("I am running")


singleton = Singleton()

使用时,直接在其他文件中导入此文件中的对象,这个对象即是单例模式的对象

1
2
3
4
from singleton import singleton

# Now, we can call the run method on the singleton object:
singleton.run()

2. 使用装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def singleton(cls):
_instance = {}

def _singleton(*args, **kwargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kwargs)
return _instance[cls]

return _singleton


@singleton
class SingletonObject:
a = 1

def __init__(self, x=0):
self.x = x


a1 = SingletonObject(2)
a2 = SingletonObject(3)

print(a1.x)
print(a2.x)

3. 使用类

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
import threading
import time


class Singleton(object):
_instance_lock = threading.Lock()

def __init__(self, *args, **kwargs):
time.sleep(1)

@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
with Singleton._instance_lock:
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance


def task(arg):
singleton_obj = Singleton.instance()
print(singleton_obj)


for i in range(10):
t = threading.Thread(target=task, args=[i, ])
t.start()

time.sleep(20)
obj = Singleton.instance()
print(obj)

这种方式实现的单例模式,使用时会有限制,以后实例化必须通过 obj = Singleton.instance()

如果用 obj = Singleton(),这种方式得到的不是单例。

4. 基于 new 方法实现

当我们实例化一个对象时,先执行类的 new 方法(我们没写时,默认调用 object.__new__),实例化对象;然后再执行类的 init 方法,对这个对象进行初始化,所有可以基于此,实现单例模式。

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
import threading


class Singleton(object):
_instance_lock = threading.Lock()

def __init__(self):
pass

def __new__(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
with Singleton._instance_lock:
if not hasattr(Singleton, "_instance"):
Singleton._instance = object.__new__(cls)
return Singleton._instance


def task(arg):
obj = Singleton()
print(obj)


for i in range(10):
t = threading.Thread(target=task, args=[i, ])
t.start()

obj1 = Singleton()
obj2 = Singleton()
print(obj1, obj2)

采用这种方式的单例模式,以后实例化对象时,和平时实例化对象的方法一样 obj = Singleton()

5. 基于 metaclass 方式实现

相关知识:

  • 类由 type 创建,创建类时,type 的 init 方法自动执行,类() 执行 type 的 call 方法(类的 new 方法,类的 init 方法)
  • 对象由类创建,创建对象时,类的 init 方法自动执行,对象()执行类的 call 方法

元类使用例子:

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
class TestType(type):
def __new__(cls, *args, **kwargs):
print("TestType.__new__")
return super().__new__(cls, *args, **kwargs)

def __init__(cls, *args, **kwargs):
print("TestType.__init__")
super().__init__(*args, **kwargs)

def __call__(cls, *args, **kwargs):
print("TestType.__call__")
return super().__call__(*args, **kwargs)


class Test(metaclass=TestType):
def __new__(cls, *args, **kwargs):
print("Test.__new__")
return super().__new__(cls, *args, **kwargs)

def __init__(self, *args, **kwargs):
print("Test.__init__")
super().__init__(*args, **kwargs)

def __call__(self, *args, **kwargs):
print("Test.__call__")


# ##### 1. 什么都不调用时输出 #####
# TestType.__new__
# TestType.__init__

# ##### 2. 实例化类时输出 #####
# TestType.__new__
# TestType.__init__
# TestType.__call__
# Test.__new__
# Test.__init__
#
# test = Test()

# ##### 3. 调用实例时输出 #####
# TestType.__new__
# TestType.__init__
# TestType.__call__
# Test.__new__
# Test.__init__
# Test.__call__
test = Test()
test()

单例模式实现如下:

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
import threading


class SingletonType(type):
_instance_lock = threading.Lock()

def __call__(cls, *args, **kwargs):
if not hasattr(cls, "_instance"):
with SingletonType._instance_lock:
if not hasattr(cls, "_instance"):
cls._instance = super(SingletonType, cls).__call__(*args, **kwargs)
return cls._instance


class Foo(metaclass=SingletonType):
def __init__(self, name):
self.name = name


print(hasattr(Foo, '_instance'))
obj1 = Foo('name')
print(hasattr(Foo, '_instance'))
print(Foo._instance)
obj2 = Foo('name')
print(obj1, obj2)

优缺点

优点:

  • 对唯一实例的受控访问
  • 单例相当于全局变量,但防止了命名空间被污染

原型模式

内容

也可以称为复制模式。本质就是克隆对象,在对象初始化操作比较复杂的情况下,很实用,这既隐藏了对象创建的细节,又能大大降低耗时,提高性能。

实现

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
import copy


class Book:
def __init__(self, name, authors, price, **rest):
"""
rest的例子有:出版商、长度、标签、出版日期
"""
self.name = name
self.authors = authors
self.price = price
# 添加其他额外属性
self.__dict__.update(rest)

def __str__(self):
format_string = ''
for key, value in self.__dict__.items():
format_string = f'{format_string}{key}: {value}, '
return format_string


class Prototype:
def __init__(self):
# 初始化一个原型列表
self.objects = dict()

def register(self, identifier, obj):
# 在原型列表中注册原型对象
self.objects[identifier] = obj

def unregister(self, identifier):
# 从原型列表中删除原型对象
del self.objects[identifier]

def clone(self, identifier, **attr):
# 根据 identifier 在原型列表中查找原型对象并克隆
found = self.objects.get(identifier)
if not found:
raise ValueError('Incorrect object identifier: {}'.format(identifier))
obj = copy.deepcopy(found)
# 用新的属性值替换原型对象中的对应属性
obj.__dict__.update(attr)
return obj


def main():
book1 = Book(
'The C Programming Language',
('Brian W. Kernighan', 'Dennis M.Ritchie'),
price=118,
publisher='Prentice Hall',
length=228, publication_date='1978-02-22',
tags=('C', 'programming', 'algorithms', 'data structures')
)

prototype = Prototype()
register_id = 'book1'

# 注册原型对象
prototype.register(register_id, book1)
book2 = prototype.clone(
register_id,
name='The C Programming Language(ANSI)',
price=48.99,
length=274,
publication_date='1988-04-01',
edition=2
)

print(book1)
print(book2)
print(f"ID book1 : {id(book1)} != ID book2 : {id(book2)}")


if __name__ == '__main__':
main()

其实这段代码在 Python 中,要实现一样的效果,并没有这么复杂。以下代码除了原型模式类删除之外,只有 main 函数有些变更。

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
import copy


class Book:
def __init__(self, name, authors, price, **rest):
"""
rest的例子有:出版商、长度、标签、出版日期
"""
self.name = name
self.authors = authors
self.price = price
# 添加其他额外属性
self.__dict__.update(rest)

def __str__(self):
format_string = ''
for key, value in self.__dict__.items():
format_string = f'{format_string}{key}: {value}, '
return format_string


def main():
book1 = Book(
'The C Programming Language',
('Brian W. Kernighan', 'Dennis M.Ritchie'),
price=118,
publisher='Prentice Hall',
length=228, publication_date='1978-02-22',
tags=('C', 'programming', 'algorithms', 'data structures')
)

# 这里我们彻底抛弃之前的原型设计模式的写法,同样的内容,经过不同的方式,可以得到同样的效果。
book2 = copy.deepcopy(book1)
book2.name = 'The C Programming Language(ANSI)'
book2.price = 48.99
book2.length = 274
book2.publication_date = '1988-04-01'
book2.edition = 2

print(book1)
print(book2)
print(f"ID book1 : {id(book1)} != ID book2 : {id(book2)}")


if __name__ == '__main__':
main()

优缺点

优点:

  • 原型模式用于创建复杂的和耗时的实例,复制一个已经存在的实例使程序运行更高效。

缺点:

  • 每一个产品类都必须配置一个克隆方法,并且这个克隆方法需要对类的功能进行整体考虑。

创建型模式小结

抽象工厂模式和建造者模式相比于简单工厂模式和工厂方法模式而言更灵活也更复杂。

通常情况下,设计以简单工厂模式或工厂方法模式开始,当你发现设计需要更大的灵活性时,则向更复杂的设计模式演化。

文章目录
  1. 1. 设计模式简介
  2. 2. 概念
    1. 2.1. 面向对象
    2. 2.2. 接口
  3. 3. 面向对象设计SOLID原则
    1. 3.1. 设计模式分类
  4. 4. 创建型模式
    1. 4.1. 简单工厂模式
      1. 4.1.1. 内容
      2. 4.1.2. 角色
      3. 4.1.3. 实现
      4. 4.1.4. 优缺点
    2. 4.2. 工厂方法模式
      1. 4.2.1. 内容
      2. 4.2.2. 角色
      3. 4.2.3. 实现
      4. 4.2.4. 优缺点
    3. 4.3. 抽象工厂模式
      1. 4.3.1. 内容
      2. 4.3.2. 角色
      3. 4.3.3. 实现
      4. 4.3.4. 优缺点
    4. 4.4. 建造者模式
      1. 4.4.1. 内容
      2. 4.4.2. 角色
      3. 4.4.3. 实现
      4. 4.4.4. 优缺点
    5. 4.5. 单例模式
      1. 4.5.1. 内容
      2. 4.5.2. 角色
      3. 4.5.3. 实现
        1. 4.5.3.1. 1. 使用模块
        2. 4.5.3.2. 2. 使用装饰器
        3. 4.5.3.3. 3. 使用类
        4. 4.5.3.4. 4. 基于 new 方法实现
        5. 4.5.3.5. 5. 基于 metaclass 方式实现
      4. 4.5.4. 优缺点
    6. 4.6. 原型模式
      1. 4.6.1. 内容
      2. 4.6.2. 实现
      3. 4.6.3. 优缺点
    7. 4.7. 创建型模式小结