软件编程之设计模式II-创建型模式

创建型模式的作用就是创建对象,说到创建一个对象,最熟悉的就是 new 一个对象,然后 set 相关属性。但是,在很多场景下,我们需要给客户端提供更加友好的创建对象的方式,尤其是那种我们定义了类,但是需要提供给其他开发者用的时候。

总的来说就是对象实例化的模式,创建型模式用于解耦对象的实例化过程。

共分为5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

简单工厂模式

不直接向高层代码暴露对象创建的实现细节,而是通过一个工厂类来负责创建产品类的实例

角色

  • 工厂角色(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
from abc import ABCMeta, abstractmethod


class Payment(metaclass=ABCMeta):
"""抽象产品角色"""
@abstractmethod
def pay(self, money):
pass


class AliPay(Payment):
"""Alipay具体产品角色"""
def __init__(self, huabei=False):
self.huabei = huabei

def pay(self, money):
if self.huabei:
print(f"使用花呗支付:¥{money}")
else:
print(f"使用支付宝支付:¥{money}")


class Wechat(Payment):
"""Wechat具体产品角色"""
def pay(self, money):
print(f"使用微信支付:¥{money}")


class FactoryPayment:
"""工厂角色"""
def create_pay(self, method):
if method == 'alipay':
return AliPay()
elif method == "wechat":
return Wechat()
elif method == 'huabei':
return AliPay(huabei=True)
else:
raise TypeError(f"不支持的支付类型:{method}")


# p = FactoryPayment().create_pay("alipay")
# p = FactoryPayment().create_pay("wechat")
p = FactoryPayment().create_pay("huabei")
p.pay(100)

工厂方法模式

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

角色

  • 抽象工厂角色(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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
from abc import ABCMeta, abstractmethod


class Payment(metaclass=ABCMeta):
"""抽象产品角色"""

@abstractmethod
def pay(self, money):
pass


class AliPay(Payment):
"""具体产品角色"""

def __init__(self, huabei=False):
self.huabei = huabei

def pay(self, money):
if self.huabei:
print(f"使用花呗支付:¥{money}")
else:
print(f"使用支付宝支付:¥{money}")


class Wechat(Payment):
"""具体产品角色"""

def pay(self, money):
print(f"使用微信支付:¥{money}")


class FactoryPayment(metaclass=ABCMeta):
"""抽象工厂角色"""

@abstractmethod
def create_pay(self):
pass


class AlipayFactoryPayment(FactoryPayment):
"""具体工厂角色"""

def create_pay(self):
return AliPay()


class WechatFactoryPayment(FactoryPayment):
"""具体工厂角色"""

def create_pay(self):
return Wechat()


class HuabeiFactoryPayment(FactoryPayment):
"""具体工厂角色"""

def create_pay(self):
return AliPay(huabei=True)


# p = AlipayFactoryPayment().create_pay()
# p = WechatFactoryPayment().create_pay()
p = HuabeiFactoryPayment().create_pay()
p.pay(100)

抽象工厂模式

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

例如:生产异步手机需要处理器和操作系统两类对象进行组装,其中每类对象都有不同的种类(如处理器有骁龙、联发科、苹果;操作系统有安卓、苹果)。对于每个具体的工厂,分别生产一部手机所需要的两个对象。

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

角色

  • 抽象工厂角色(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
from abc import ABCMeta, abstractmethod


# 抽象产品
class PhoneCpu(metaclass=ABCMeta):
"""cpu"""

@abstractmethod
def show_cpu(self):
pass


class PhoneOS(metaclass=ABCMeta):
"""os"""

@abstractmethod
def show_os(self):
pass


# 具体产品
class SnapdragonCpu(PhoneCpu):
def show_cpu(self):
print("骁龙处理器")


class MtkCpu(PhoneCpu):
def show_cpu(self):
print("联发科处理器")


class AppleCpu(PhoneCpu):
def show_cpu(self):
print("苹果处理器")


class AndroidOs(PhoneOS):
def show_os(self):
print("安卓操作系统")


class IosOs(PhoneOS):
def show_os(self):
print("苹果操作系统")


# 抽象工厂
class Factory(metaclass=ABCMeta):
@abstractmethod
def make_cpu(self):
pass

@abstractmethod
def make_os(self):
pass


# 具体工厂
class HuaweiFactory(Factory):
def make_cpu(self):
return MtkCpu()

def make_os(self):
return AndroidOs()


class ApppleFactory(Factory):
def make_cpu(self):
return AppleCpu()

def make_os(self):
return IosOs()


# 客户端(高层代码)
class Phone:
def __init__(self, cpu, os):
self.cpu = cpu
self.os = os

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


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


# 生产一个华为手机
huawei = make_phone(HuaweiFactory())
huawei.show()
print("-------------------------")
# 生产一个苹果手机
apple = make_phone(ApppleFactory())
apple.show()

创建者模式

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

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

角色

  • 抽象建造者(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
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
from abc import ABCMeta, abstractmethod

# 客户端
class Player:
def __init__(self, hair=None, face=None, skin=None, body=None):
self.hair = hair
self.face = face
self.skin = skin
self.body = body

def show(self):
print("角色参数如下:")
print("发型:", self.hair)
print("表情:", self.face)
print("肤色:", self.skin)
print("身体:", self.body)


# 抽象产品

class Hair(metaclass=ABCMeta):
@abstractmethod
def show_hair(self):
pass


class Face(metaclass=ABCMeta):
@abstractmethod
def show_face(self):
pass


class Skin(metaclass=ABCMeta):
@abstractmethod
def show_skin(self):
pass


class Body(metaclass=ABCMeta):
@abstractmethod
def show_body(self):
pass


# 具体产品
class SortHair(Hair):
def show_hair(self):
return "短发"


class LongHair(Hair):
def show_hair(self):
return "长发"


class GrimaceFace(Face):
def show_face(self):
return "冷酷表情"


class PhotophobiaFace(Face):
def show_face(self):
return "恐怖表情"


class YellowSkin(Skin):
def show_skin(self):
return "黄皮肤"


class WhiteSkin(Skin):
def show_skin(self):
return "白皮肤"


class BlackSkin(Skin):
def show_skin(self):
return "黑皮肤"


class SexyBoby(Body):
def show_body(self):
return "性感的身材"


class StrongBoby(Body):
def show_body(self):
return "强壮的身材"


# 抽象建造者
class PlayerBuilder(metaclass=ABCMeta):
def __init__(self):
self.player = Player()

@abstractmethod
def build_hair(self):
pass

@abstractmethod
def build_face(self):
pass

@abstractmethod
def build_skin(self):
pass

@abstractmethod
def build_body(self):
pass


# 具体建造者
class Humen(PlayerBuilder):
"""人族"""

def build_hair(self):
self.player.hair = LongHair().show_hair()

def build_face(self):
self.player.face = GrimaceFace().show_face()

def build_skin(self):
self.player.skin = YellowSkin().show_skin()

def build_body(self):
self.player.body = SexyBoby().show_body()


class Orc(PlayerBuilder):
"""兽族"""

def build_hair(self):
self.player.hair = SortHair().show_hair()

def build_face(self):
self.player.face = PhotophobiaFace().show_face()

def build_skin(self):
self.player.skin = BlackSkin().show_skin()

def build_body(self):
self.player.body = StrongBoby().show_body()


# 指挥者
class PlayerDirector:
def build_player(self, builder):
builder.build_hair()
builder.build_face()
builder.build_skin()
builder.build_body()
return builder.player


# p = PlayerDirector().build_player(Orc())
p = PlayerDirector().build_player(Humen())
p.show()

开启了调试模式可以看到指挥者对构造顺序的控制

单例模式

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

角色

  • 单例(Singleton)

优点

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

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Signton:
def __new__(cls, *args, **kwargs):
if not hasattr(cls, "_instance"):
cls._instance = super().__new__(cls)
return cls._instance

class Test(Signton):
def __init__(self, a):
self.a = a


aa = Test(11)
bb = Test(22)
print("id(aa):", id(aa))
print("id(bb):", id(bb))
print("aa.a:", aa.a)
print("bb.a:", bb.a)

输出结果:

1
2
3
4
id(aa): 1999229563848
id(bb): 1999229563848
aa.a: 22
bb.a: 22

创建型模式总结