结构型模式:适配器模式、桥模式、组合模式、装饰模式、外观模式、享元模式、代理模式。
一、适配器模式
将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的的那些类可以一起工作。
1、适配器模式实现
(1)类适配器:使用多继承
from abc import ABCMeta, abstractmethod class Payment(metaclass=ABCMeta): # abstract class @abstractmethod def pay(self, money): pass class Alipay(Payment): def pay(self, money): print("支付宝支付%d元." % money) class WechatPay(Payment): def pay(self, money): print("微信支付%d元." % money) class BankPay: def cost(self, money): # 没有使用pay方法 print("银联支付%d元." % money) # 第一种适配器模式:使用多继承 class NewBankPay(Payment, BankPay): def pay(self, money): # 把本来不兼容的接口cost转为兼容的接口pay self.cost(money) p = NewBankPay() p.pay(100) # 银联支付100元.
使用这种适配器模式,就将本来不兼容的接口cost转变为兼容的接口pay。
(2)对象适配器:使用组合
组合:在一个类里放入另外一个类的对象。
class A: pass class B: def __init__(self): self.a = A()
根据组合来实现代码复用,实现多个不兼容接口的适配:
from abc import ABCMeta, abstractmethod class Payment(metaclass=ABCMeta): # abstract class @abstractmethod def pay(self, money): pass class Alipay(Payment): def pay(self, money): print("支付宝支付%d元." % money) class WechatPay(Payment): def pay(self, money): print("微信支付%d元." % money) class BankPay: def cost(self, money): # 没有使用pay方法 print("银联支付%d元." % money) class ApplePay: def cost(self, money): print("苹果支付%d元" % money) # 第一种适配器模式:使用多继承 # class NewBankPay(Payment, BankPay): # def pay(self, money): # 把本来不兼容的接口cost转为兼容的接口pay # self.cost(money) # 第二种适配器模式:使用组合 class PaymentAdapter(Payment): def __init__(self, payment): self.payment = payment def pay(self, money): self.payment.cost(money) p = PaymentAdapter(ApplePay()) # 使用适配器进行苹果支付 p.pay(100) # 苹果支付100元
这种使用组合来实现的适配器模式就叫做对象适配器。
2、适配器模式总结
角色:
- 目标接口(Target)
- 待适配的类(Adaptee)
- 适配器(Adapter)
适用场景:
- 想使用一个已经存在的类,而它的接口不符合你的要求。
- (对象适配器)想使用一些已经存在的子类,但不可能对每一个都进行子类化以匹配它们的接口,对象适配器可以适配它的父类接口。
二、桥模式
将一个事物的两个维度分离,使其都可以独立地变化。
1、桥模式示例
from abc import ABCMeta, abstractmethod class Shape(metaclass=ABCMeta): # 形状这个维度 def __init__(self, color): self.color = color # 组合的方式让形状和颜色实现耦合(松耦合),好处是两个维度都可以任意去扩展。 @abstractmethod def draw(self): pass class Color(metaclass=ABCMeta): # 颜色这个维度 @abstractmethod def paint(self, shape): # 着色 pass class Rectangle(Shape): # 长方形 name = "长方形" def draw(self): # 长方形逻辑 self.color.paint(self) # 给paint函数传入自己(形状:长方形) class Circle(Shape): # 圆形 name = "圆形" def draw(self): # 圆形逻辑 self.color.paint(self) # 传入圆形 class Red(Color): def paint(self, shape): print("红色的%s" % shape.name) class Green(Color): def paint(self, shape): print("绿色的%s" % shape.name) shape = Rectangle(Red()) shape.draw() # 红色的长方形 shape2 = Circle(Green()) shape2.draw() # 绿色的圆形
如上所示:组合的方式让形状和颜色实现耦合(松耦合),在形状和颜色这两个维度上都可以任意去扩展。
2、桥模式总结
角色:
- 抽象(Abstraction):Shape
- 细化抽象(RefinedAbstraction):Rectangle、Circle....
- 实现者(Implementor):Color
- 具体实现者(ConcreteImplementor):Red、Green...
应用场景:
- 当事物有两个维度上的表现,两个维度都可能扩展时。
优点:
- 抽象和实现相分离
- 优秀的扩展能力
三、组合模式
将对象组合成树形结构以表示“部分——整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
1、组合模式示例
from abc import ABCMeta, abstractmethod # 抽象组件 class Graphic(metaclass=ABCMeta): # 图像接口 @abstractmethod def draw(self): pass # 叶子组件 class Point(Graphic): def __init__(self, x, y): self.x = x self.y = y def __str__(self): return "点(%s, %s)" % (self.x, self.y) def draw(self): print(str(self)) # 把自己通过__str__方法变成字符串打印 # 叶子组件 class Line(Graphic): def __init__(self, p1, p2): self.p1 = p1 self.p2 = p2 def __str__(self): return "线段[%s, %s]" % (self.p1, self.p2) def draw(self): print(str(self)) # 把自己通过__str__方法变成字符串打印 # 复合组件 class Picture(Graphic): def __init__(self, iterable): self.children = [] for g in iterable: self.add(g) def add(self, graphic): # 添加节点 self.children.append(graphic) def draw(self): # 复杂图形要将孩子节点都绘制出来 print("------复合图形------") for g in self.children: g.draw() print("------复合图形------") # 客户端 p1 = Point(2,3) l1 = Line(Point(1,1), Point(2,2)) l2 = Line(Point(3,4), Point(2,8)) pic1 = Picture([p1, l1, l2]) p2 = Point(4,4) l3 = Line(Point(1,1), Point(0,0)) pic2 = Picture([p2, l3]) pic = Picture([pic1, pic2]) pic.draw() """ ------复合图形------ ------复合图形------ 点(2, 3) 线段[点(1, 1), 点(2, 2)] 线段[点(3, 4), 点(2, 8)] ------复合图形------ ------复合图形------ 点(4, 4) 线段[点(1, 1), 点(0, 0)] ------复合图形------ ------复合图形------ """
2、组合模式总结
角色:
- 抽象组件(Component)
- 叶子组件(Leaf)
- 复合组件(Composite)
- 客户端(Client)
适用场景:
- 表示对象的“部分——整体”层次结构(特别是结构是递归的)
- 希望用户忽略组合对象与单个对象的不同,用户统一地使用组合结构中的所有对象
优点:
- 定义了包含基本对象和组合对象的类层次结构
- 简化客户端代码,即客户端可以一致地适用组合对象和单个对象
- 更容易增加新类型的组件(比如添加圆形等)
四、外观模式
为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
1、外观模式示例
class CPU: # 子系统 def run(self): print("CPU开始运行") def stop(self): print("CPU停止运行") class Disk: # 子系统 def run(self): print("硬盘开始工作") def stop(self): print("硬盘停止工作") class Memory: # 子系统 def run(self): print("内存开始工作") def stop(self): print("内存停止工作") 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() computer = Computer() computer.run() computer.stop() """ CPU开始运行 硬盘开始工作 内存开始工作 CPU停止运行 硬盘停止工作 内存停止工作 """
外观模式比较简单,目的是让用户不直接去操作子系统中方法。封装一个高级的外观来进行调用。Client就不需要知道是哪几部分及哪几部分的哪几个方法来实现的。
缩小了底层代码和高层代码的耦合。子系统需要改造的话,只需修改外观模式对好接口,就不会出现问题。
2、外观模式总结
角色:
- 外观(facade)
- 子系统类(subsystem classes)
优点:
- 减少了系统相互依赖(解耦)
- 提高了灵活性(更加方便和灵活提供客户端调用)
- 提高了安全性(防止了遗漏)
五、代理模式
为其他对象提供一种代理以控制对这个对象的访问。
1、应用场景
(1)远程代理:为远程的对象提供代理
略
(2)虚代理:根据需要创建很大的对象
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, filename): self.filename = filename f = open(filename, 'r', encoding='utf-8') print("读取文件内容") self.content = f.read() # 读取文件中内容并保存 f.close() def get_content(self): return self.content def set_content(self, content): f = open(self.filename, 'w', encoding="utf-8") f.write(content) f.close() # 虚代理预期:构造对象时不占用空间保存content,在调用get_content方法时才真正从文件读取信息。 class VirtualProxy(Subject): # 虚代理也需要实现Subject接口 def __init__(self, filename): self.filename = filename self.subj = None def get_content(self): if not self.subj: # 第一次访问,self.subj为None self.subj = RealSubject(self.filename) # 组合方式传递self.filename给RealSubject,由它来创建真实对象 return self.subj.get_content() def set_content(self, content): if not self.subj: self.subj = RealSubject(self.filename) return self.subj.set_content() # subj = RealSubject("test.txt") # 仅仅执行到这里,content也已经在占用内存了 # subj.get_content() sub2 = VirtualProxy("test.txt") # 此时不占用内存 print(sub2.get_content()) """ 读取文件内容 测试代理模式 """
虚代理在构造对象时不占用空间保存content,在调用get_content方法时才真正从文件读取信息。
(3)保护代理:控制对原始对象的访问,用于对象有不同访问权限时
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, filename): self.filename = filename f = open(filename, 'r', encoding='utf-8') print("读取文件内容") self.content = f.read() # 读取文件中内容并保存 f.close() def get_content(self): return self.content def set_content(self, content): f = open(self.filename, 'w', encoding="utf-8") f.write(content) f.close() class ProtectedProxy(Subject): # 保护代理 def __init__(self, filename): self.subj = RealSubject(filename) def get_content(self): return self.subj.get_content() def set_content(self, content): raise PermissionError("无写入权限") subj3 = ProtectedProxy("test.txt") print(subj3.get_content()) # 读文件没有问题 subj3.set_content() # 写文件直接抛出异常
保护代理允许再访问对象时有一些不同访问权限。
2、代理模式总结
角色:
- 抽象实体(Subject)
- 实体(RealSubject)
- 代理(Proxy)
优点:
- 远程代理:可以隐藏对象位于远程地址空间的事实
- 虚代理:可以进行优化,例如根据要求创建对象
- 保护代理:允许在访问一个对象时有一些父级的内务处理