把类或对象结合在一起形成一个更大的结构
可分为以下几种:适配器模式、桥模式、组合模式、装饰模式、外观模式、享元模式、代理模式。
适配器模式
将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
实现方式
角色
- 目标接口(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}")
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
class Subject(metaclass=ABCMeta): @abstractmethod def get_content(self): pass
@abstractmethod def set_content(self, content): pass
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)
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)
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("没有写入权限")
p = ProtectProxy('x.txt') print(p.get_content()) p.set_content('bbbb')
|