软件编程之设计模式III-结构性模式

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

可分为以下几种:适配器模式、桥模式、组合模式、装饰模式、外观模式、享元模式、代理模式。

适配器模式

将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

实现方式

  • 类适配器:使用多继承
  • 对象适配器:使用组合

角色

  • 目标接口(Target)
  • 带适配的类(Adapter)
  • 适配器(Adapter)

适用场景

  • 想使用一个已经存在的类,而它的接口不符合你的要求
  • (对象适配器)想使用一些已经存在的子类,但不可能对每一个都进行子类化以适配它们的接口。对象适配器可以适配它的弗雷借口。

示例

初始代码

假设我们目前使用pay方法支付,后续有个程序员不知道规则,用别的方法名实现了苹果支付,那我们还想统一使用pay方法用来支付要怎么办呢?

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


class Payment(metaclass=ABCMeta):
@abstractmethod
def pay(self, money):
pass


class Alipay(Payment):
def pay(self, money):
print(f"使用支付宝支付:¥{money}")


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


class Applepay:
def defray(self, dollar):
print(f"使用苹果支付:${dollar}")

# 类适配器 我们使用一个适配器类包裹下就能正常使用pay方法了
class ApplyPayment(Payment, Applepay):
def pay(self, money):
self.defray(money)

p = ApplyPayment()
p.pay(200)

进阶

假如这位同学又写了一个银联支付,仍然使用的是defray做的支付逻辑,怎么统一使用pay呢?我们总不能再创建一个适配器类包括以下吧,我们采用组合的方式试下呗。

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


class Payment(metaclass=ABCMeta):
@abstractmethod
def pay(self, money):
pass


class Alipay(Payment):
def pay(self, money):
print(f"使用支付宝支付:¥{money}")


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


class Applepay:
def defray(self, dollar):
print(f"使用苹果支付:${dollar}")

class Bankpay:
def defray(self, dollar):
print(f"使用银联支付:¥{dollar}")

# 对象适配器
class PaymentAdapter(Payment):
def __init__(self, pay_obj):
self.pay_obj = pay_obj

def pay(self, money):
self.pay_obj.defray(money)



p = PaymentAdapter(Bankpay())
p.pay(999)


p = PaymentAdapter(Applepay())
p.pay(999)

桥模式

使一个事物的两个维度分离,使其都可以独立地变化。

角色

  • 抽象(Abstraction)
  • 细化抽象(RefinedAbstraction)
  • 实现者(Implementor)
  • 具体实现者(ConcreteImplementor)

应用场景

当事物有两个维度上的表现,两个维度都可能扩展时。

优点

  • 抽象和实现相分离
  • 优秀的扩展能力

示例

如下,想绘制一个形状颜色的组合要建立相应的类,如果要添加对应一个颜色或形状更麻烦了,要把对应的形状和颜色类全部建立一遍,感觉把自己都说绕了,

简单来说就是如果我添加一个绿色的类,那我还要建立绿色的圆类、绿色矩形类,如果有更多的形状,要建的类就更多了。

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


class Shape(metaclass=ABCMeta):
@abstractmethod
def draw(self):
pass


class Rectangle(Shape):
def draw(self):
print("矩形")


class Circle(Shape):

def draw(self):
print("圆形")


class Color(metaclass=ABCMeta):
@abstractmethod
def paint(self):
pass


class Red(Color):
def paint(self):
print("红色")


class Blue(Color):
def paint(self):
print("蓝色")


class RedCircle(Red, Circle):
# 红色圆逻辑
pass


class RedRectangle(Red, Circle):
# 红色矩形逻辑
pass


class BlueRectangle(Blue, Circle):
# 蓝色矩形逻辑
pass


class BlueCircle(Blue, Circle):
# 蓝色圆逻辑
pass

进阶

我们使用了组合的方式,只建立所有形状和颜色类即可,如果要增加一个颜色和其他所有形状的搭配也只需要增加一个颜色类即可,如下的Yellow类。

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

# 抽象
class Shape(metaclass=ABCMeta):
def __init__(self, color_instance):
self.color = color_instance
@abstractmethod
def draw(self):
pass

# 细化抽象
class Rectangle(Shape):
name = "矩形"
def draw(self):
# 具体画矩形的逻辑
self.color.paint(self)

# 细化抽象
class Circle(Shape):
name = "圆形"
def draw(self):
# 具体画圆的逻辑
self.color.paint(self)

# 实现
class Color(metaclass=ABCMeta):
@abstractmethod
def paint(self, shape_instance):
pass

# 具体实现者
class Red(Color):
name = "红色"
def paint(self, shape_instance):
print(f"{self.name}-{shape_instance.name}")

# 具体实现者
class Blue(Color):
name = "蓝色"
def paint(self, shape_instance):
print(f"{self.name}-{shape_instance.name}")

# 具体实现者
class Yellow(Color):
name = "黄色"
def paint(self, shape_instance):
print(f"{self.name}-{shape_instance.name}")


# 蓝色矩形
p = Rectangle(Blue())
p.draw()

# 红色圆
c = Circle(Red())
c.draw()

# 黄色矩形
y = Rectangle(Yellow())
y.draw()

组合模式

将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

角色

  • 抽象组件(Component)
  • 叶子组件(Leaf)
  • 复合组件(Composite)
  • 客户端(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
from abc import ABCMeta, abstractmethod
from typing import List

# 抽象组件
class Graphic(metaclass=ABCMeta):
@abstractmethod
def draw(self):
pass

# 叶子组件
class Point(Graphic):
"""点"""

def __init__(self, x: int, y: int):
self.x = x
self.y = y

def __str__(self):
return f"【点】坐标为:({self.x}, {self.y})"

def draw(self):
print(str(self))

# 叶子组件
class Line(Graphic):
"""线段"""

def __init__(self, origin: Point, destination: Point):
self.origin = origin
self.destination = destination

def __str__(self):
return f"【线段】起点:{self.origin};终点:{self.destination}"

def draw(self):
print(str(self))

# 复合组件
class Picture(Graphic):
def __init__(self, childs: List[Graphic] = []):
self.childs = []
for item in childs:
self.add(item)

def add(self, obj: Graphic):
self.childs.append(obj)

def draw(self):
print("---------复杂图形开始-------------")
for item in self.childs:
item.draw()
print("---------复杂图形结束-------------")




# 客户端(高层代码)
p = Point(100, 100)
l = Line(Point(1, 2), Point(4, 5))

pic = Picture([p, l])
pic.draw()

print("***********************************************")

l2 = Line(Point(99, 99), Point(999, 999))
pic2 = Picture()
pic2.add(l2)
pic2.add(pic)
pic2.draw()

输出结果手动做了缩进有和,方便大家对组合结构看的更清晰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
---------复杂图形开始-------------
坐标为:(100, 100)
【线段】起点:【点】坐标为:(1, 2);终点:【点】坐标为:(4, 5)
---------复杂图形结束-------------

***********************************************

---------复杂图形开始-------------
【线段】起点:【点】坐标为:(99, 99);终点:【点】坐标为:(999, 999)
---------复杂图形开始-------------
坐标为:(100, 100)
【线段】起点:【点】坐标为:(1, 2);终点:【点】坐标为:(4, 5)
---------复杂图形结束-------------
---------复杂图形结束-------------


我们尝试再增加一个叶子组件:圆,来组合到一起

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 abc import ABCMeta, abstractmethod
from typing import List


# 抽象组件
class Graphic(metaclass=ABCMeta):
@abstractmethod
def draw(self):
pass


# 叶子组件
class Point(Graphic):
"""点"""

def __init__(self, x: int, y: int):
self.x = x
self.y = y

def __str__(self):
return f"【点】坐标为:({self.x}, {self.y})"

def draw(self):
print(str(self))


# 叶子组件
class Line(Graphic):
"""线段"""

def __init__(self, origin: Point, destination: Point):
self.origin = origin
self.destination = destination

def __str__(self):
return f"【线段】起点:{self.origin};终点:{self.destination}"

def draw(self):
print(str(self))


# 叶子组件
class Circle(Graphic):
"""圆"""

def __init__(self, point_center: Point, radius: int):
self.point_center = point_center
self.radius = radius

def __str__(self):
return f"【圆】圆心:{self.point_center};半径:{self.radius}"

def draw(self):
print(str(self))


# 复合组件
class Picture(Graphic):
def __init__(self, childs: List[Graphic] = []):
self.childs = []
for item in childs:
self.add(item)

def add(self, obj: Graphic):
self.childs.append(obj)

def draw(self):
print("---------复杂图形开始-------------")
for item in self.childs:
item.draw()
print("---------复杂图形结束-------------")


# 客户端(高层代码)
p = Point(100, 100)
l = Line(Point(1, 2), Point(4, 5))

pic = Picture([p, l])
pic.draw()

print("***********************************************")

c = Circle(Point(66, 66), 10)
l2 = Line(Point(99, 99), Point(999, 999))
pic2 = Picture()
pic2.add(l2)
pic2.add(pic)
pic2.add(c)
pic2.draw()

输出结果,我们只需要增加一个Circle类,就能和其他叶子组件组合成一个更复杂的图形。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
---------复杂图形开始-------------
【点】坐标为:(100, 100)
【线段】起点:【点】坐标为:(1, 2);终点:【点】坐标为:(4, 5)
---------复杂图形结束-------------

***********************************************

---------复杂图形开始-------------
【线段】起点:【点】坐标为:(99, 99);终点:【点】坐标为:(999, 999)
---------复杂图形开始-------------
【点】坐标为:(100, 100)
【线段】起点:【点】坐标为:(1, 2);终点:【点】坐标为:(4, 5)
---------复杂图形结束-------------
【圆】圆心:【点】坐标为:(66, 66);半径:10
---------复杂图形结束-------------

装饰模式

外观模式

为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用

角色

  • 外观(Facade)
  • 子系统类(Subsystem classes)

优点

  • 减少系统相互依赖
  • 提高了灵活性
  • 提高了安全性

示例

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
# 子系统类
class Cpu:
def run(self):
print("开启cpu")

def stop(self):
print("关闭cpu")

# 子系统类
class Disk:
def run(self):
print("开启Disk")

def stop(self):
print("关闭Disk")

# 子系统类
class Memory:
def run(self):
print("开启Memory")

def stop(self):
print("关闭Memory")

# 外观
class Computer:
def __init__(self):
self.cpu = Cpu()
self.disk = Disk()
self.memory = Memory()

def run(self):
self.cpu.run()
self.disk.run()
self.memory.run()

def stop(self):
self.cpu.stop()
self.disk.stop()
self.memory.stop()


# 客户端(高层代码)
c = Computer()
c.run()
c.stop()

享元模式

代理模式

为其他对象提供一种代理以控制对这个对象的访问

应用场景

  • 远程代理:为远程的对象提供代理
  • 虚代理:根据需要创建很大的对象
  • 保护代理:控制对原始对象的访问,用于对象有不同的访问权限时

角色

  • 抽象实体(Subject)
  • 实体(RealSubject)
  • 代理(Proxy)

优点

  • 远程代理:可以隐藏对象位于远程地址空间的事实
  • 虚代理:可以进行优化,例如根据要求创建对象
  • 保护代理:允许在访问一个对象时有一些附加的内务处理

示例

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


# 抽象实体(Subject)
class Subject(metaclass=ABCMeta):
@abstractmethod
def get_content(self):
pass

@abstractmethod
def set_content(self, content):
pass


# 实体(RealSubject)
class RealSubject(Subject):
def __init__(self, file_name):
self.file = file_name
with open(self.file, "r+") as f:
print("开始读取文件内容:")
self.content = f.read()

def get_content(self):
return self.content

def set_content(self, content):
with open(self.file, "w+") as f:
f.write(content)


# 代理(Proxy)-模拟虚代理
class VirtualProxy(Subject):
def __init__(self, file_name):
self.file = file_name

def get_content(self):
if getattr(self, "subject", None) is None:
self.subject = RealSubject(self.file)
return self.subject.get_content()

def set_content(self, content):
if getattr(self, "subject", None) is None:
self.subject = RealSubject(self.file)
self.subject.set_content(content)


# 代理(Proxy)-模拟保护代理
class ProtectProxy(Subject):
def __init__(self, file_name):
self.file = file_name

def get_content(self):
if getattr(self, "subject", None) is None:
self.subject = RealSubject(self.file)
return self.subject.get_content()

def set_content(self, content):
raise PermissionError("没有写入权限")

# 实体,实例化时就将文件内容直接读取到了内存,造成内存占用过大
# r = RealSubject('x.txt')
# print(r.get_content())

# 虚代理,实例化时并不直接将文件读取之内存,实际访问get_content时才会将内容读取到内存
# v = VirtualProxy('x.txt')
# print(v.get_content())

# 保护代理,限制访问权限,只能访问某些功能等
p = ProtectProxy('x.txt')
print(p.get_content())
p.set_content('bbbb')