目录
一、装饰器模式
1.1 概述
- 定义:允许向一个现有的对象添加新的功能,同时又不改变其结构
- 适用场景:需要动态地增加或撤销功能时,有大量独立的扩展,为支持每一种组合继承将产生大量的子类
- 设计要点:注意装饰的顺序,用装饰的方式拓展功能容易出错,调试也更困难
1.2 类图
- 函数实现:传送门(第四章)
- 类实现:传送门(第2.2.6节)
二、外观模式
2.1 概述
- 定义:又名门面模式,为子系统中的一组接口提供一个一致的界面
- 设计要点:无法阻止客户端调用子系统的接口,降低了系统功能的灵活性
- 适用场景:
- 客户端解耦:将子系统与客户端解耦
- 重构解耦:重构系统时将系统分层,各层用外观角色对接
- 擦屁股:保持原系统为一层,打包一个接口,继续开发后续
2.2 类图
- 角色
- 外观角色(Façade):这个类一般不负责具体的业务逻辑,只是一个委托类,具体的业务逻辑由子系统完成
- 子系统(SubSystem):由多个类组成的具有某一特定功能的子系统
2.3 实现
- 代码示例
class CPU: def run(self): print("CPU开始工作") class Memory: def run(self): print("内存开始工作") class Computer: def __init__(self): self.cpu = CPU() self.memory = Memory() def start(self): self.cpu.run() self.memory.run() if __name__ == "__main__": computer = Computer() computer.start()
三、组合模式
3.1 概述
- 定义:将对象组合成树形结构以表示“部分-整体”的层次结构,
- 设计要点:
- 了解对象的组成结构
- 各名词:组合模式是一种具有层次关系的树形结构,不能再分的叶子节点是最小的逻辑单元;具有子节点(由多个子组件组成)的组件称为也就是组合对象;
- 限制:层次结构太深的场景中,组合结构会变得太庞杂
- 适用场景:
- 对象行为:组合对象与单个对象具有相同或类似行为(方法)
- 对象关系:对象之间具有明显的“部分-整体”的关系时,或者具有层次关系
3.2 类图
- 角色:
- 组件的基类(Component):定义统一的方法feature、isComposite、isComposite用于判断一个组件是否为复合组件
- 具体的组件:ComponentImplA、ComponentImplB
- 复合组件(Composite):本身也是一个组件,因为它也实现了feature方法
3.3 实现
- 代码示例
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 f"点({self.x},{self.y})" def draw(self): print(str(self)) # 复合节点:两个点p1、p2是线的子节点 class Line(Graphic): def __init__(self, p1, p2): self.p1 = p1 self.p2 = p2 def __str__(self): return f"线段({self.p1},{self.p2})" def draw(self): print(str(self)) # 顶层复合节点:所有传入的iterable对象都是Picture对象子节点 class Picture(Graphic): def __init__(self, iterable): # 初始化对象时遍历iterable向Picture添加对象 self.children = [] for elem in iterable: self.add(elem) def add(self, graphic): self.children.append(graphic) def draw(self): # 遍历运行每个对象的draw方法 for elem in self.children: elem.draw() # 客户端 if __name__ == "__main__": # 组合pic1的树形对象组合 p1 = Point(0, 0) l1 = Line(Point(1, 1), Point(2, 2)) l2 = Line(Point(3, 3), Point(4, 4)) pic1 = Picture([p1, l1, l2]) # 组合pic2的树形对象组合 p2 = Point(5, 5) l3 = Line(Point(6, 6), Point(7, 7)) pic2 = Picture([p2, l3]) # 组合pic树形对象组合 pic = Picture([pic1, pic2]) # 递归的调用各个子对象的draw方法 pic.draw() ########################################## >>> 点(0,0) >>> 线段(点(1,1),点(2,2)) >>> 线段(点(3,3),点(4,4)) >>> 点(5,5) >>> 线段(点(6,6),点(7,7))
四、适配器模式
4.1 概述
- 定义:将一个类的接口变成客户端所期望的另一种接口,从而使原本因接口不匹配而无法一起工作的两个类能够在一起工作
- 设计要点:过多地使用适配器,容易使代码结构混乱
- 适用场景:
- 适配老系统:现有系统需要使用老系统的类,而这些类的接口不符合现有系统的要求
- 适配第三方:增加接入第三方的接口或SDK
4.2 类图
- 继承方式实现
- 组合方式实现(推荐)
- 角色
- 目标接口(Target):提供给用户调用的接口抽象
- 待适配的类(Adaptee):要进行适配的对象类
- 适配器(Adapter):是对Adaptee的适配,它将 Adaptee 的对象转换(包装)成符合 Target 接口的对象
4.3 实现
-
类级实现
class OldCoding: def show_msg(self): print("调用了老代码的接口") class NewCoding: def print_msg(self): print("调用了新代码的接口") # 适配器与新类的方法同名 class CodingAdapter(OldCoding, NewCoding): # 因多继承,可以直接调用方法 def print_msg(self): self.show_msg() if __name__ == "__main__": # 新类的新方法 p1 = NewCoding() p1.print_msg() # 适配器的同名方法 p2 = CodingAdapter() p2.print_msg()
-
对象级实现(推荐)
# OldCoding、NewCoding跟类级实现相同 class OldCoding: def show_msg(self): print("调用了老代码的接口") class NewCoding: def print_msg(self): print("调用了新代码的接口") # 适配器同新类方法同名 class CodingAdapter(OldCoding): # code为OldCoding对象,即待适配对象 def __init__(self, code): self.code = code # 同名的方法内部调用了老类的方法 # 传入的是老类的类对象 def print_msg(self): self.code.show_msg() if __name__ == "__main__": # 新类的新方法 c1 = NewCoding() c1.print_msg() # 适配器用新类的方法,内部调用老类的方法 # 适配器接受的是老类的对象 c2 = CodingAdapter(OldCoding()) c2.print_msg()
五、桥梁模式
5.1 概述
- 定义:将抽象和实现解耦,使得两者可以独立地变化,与策略模式相似
- 设计要点:注意与策略模式对比,桥梁模式为结构型模式,关注的是抽象和实现的分离,使得它们可以独立地发展;而策略模式为行为型模式关注的是对算法、规则的封装,使得算法可以独立于使用它的用户而变化
- 适用场景:
- 一个产品(或对象)有多种分类和多种组合,即两个(或多个)独立变化的维度,每个维度都希望独立进行扩展
- 因为使用继承或因为多层继承导致系统类的个数急剧增加的系统,可以改用桥接模式来实现。
5.2 类图
- 角色
- 抽象化角色(Abstraction):主要职责是定义该角色行为,同时保存一个对实现化角色的引用
- 修正抽象化角色:它引用实现化角色对抽象化角色进行修正
- 实现化角色(Implementor):它是接口或者抽象类,定义角色必需的行为和属性
- 具体实现化角色:它实现接口或抽象类定义的方法和属性
- 应用场景:适合与两个维度以后都会扩展,如{a,b,c}与{1,2,3}组合,继承需要写9个类,桥只需要6个,扩展的话得指数级增长
5.2 实现
- 代码示例
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 Circle(Shape): name = "圆形" # 具体类里交叉调用:形状类里调用颜色方法 def draw(self): self.color.paint(self) # 可以扩展颜色,符合对扩展开放,对修改封闭原则 # 还可以实现红、蓝等类,只要以黑类为模版即可 class Black(Color): # 具体类里交叉调用:颜色类里调用形状属性 def paint(self, shape): print("黑色的", shape.name) if __name__ == "__main__": # paint方法是包在Black实例对象里传给Circle类初始化形状对象的 s = Circle(Black()) # 此处耦合,在具体类实现的层级耦合 # s为Circle实例 # 其方法draw调用抽象基类的init方法存的Color实例的paint方法 # paint方法又用到Circle类的name属性 s.draw()
六、享元模式
6.1 概述
- 定义:运用共享技术有效地支持大量细粒度对象的复用,对内存友好,对复杂度不友好
- 设计要点:
- 区分内外部状态:内部状态,享元对象内部并且不会随环境改变而改变的状态;外部状态,享元对象的外部状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入享元对象内部
- 对象:必须是轻量级、细粒度的对象
- 适用场景:一个系统有大量相同或者相似的对象,由于这类对象的大量使用,造成内存的大量耗费;缓存场景
6.2 类图
- 角色
- 享元对象(Flyweight):即你期望用来共享的对象,享元对象必须是轻量级对象,也就是细粒度对象
- 享元工厂(FlyweightFactory):享元模式的核心角色,负责创建和管理享元对象,提供一个享元池
6.3 实现
- 代码示例
class ColorFlyWeight: """颜色享元类""" def __init__(self, name): self.name = name class ColorFactory: """颜色创建工厂""" def __init__(self): # 此处存储待共享的对象 self.color = {} # 享元模式针对只读对象,在多次读取的过程不重复创建对象 def get_color(self, color_name): # 字典的方法get color = self.color.get(color_name) # 如果存在于init的color中,直接返回 if color: print("调用已存在的颜色对象:" + color_name) return color # 否则创建对象,并将对象以键值对方式加入init方法中的color字典中 print("没有此颜色,创建颜色:" + color_name) color = ColorFlyWeight(color_name) self.color[color_name] = color return color if __name__ == "__main__": factory = ColorFactory() Red_color = factory.get_color("红色") Black_color = factory.get_color("黑色") Red2_color = factory.get_color("红色") ###################################################### >>> 没有此颜色,创建颜色:红色 >>> 没有此颜色,创建颜色:黑色 >>> 调用已存在的颜色对象:红色
七、代理模式
7.1 概述
- 定义:为一个对象提供一个替身,以控制对这个对象的访问,隐藏被代理对象的部分功能和服务,也可增加额外的功能
- 设计要点:防止过于臃肿,拖慢访问速度,或过于复杂
- 应用场景
- 远程代理:不想或者不能直接引用一个对象时,如在移动端加载网页信息时预留占位符
- 特殊用途代理:用于对象应该具有不同访问权限的场景,控制对原始对象的访问
7.2 类图
- 角色:
- 主题的抽象基类(Subject):负责定义操作、活动、任务的接口类
- 代理类(ProxySubjec):代理RealSubject的功能,调用被代理类,并附加自己的操作
- 真实主题类(RealSubject):Subject的具体实现类
7.3 实现
- 代码示例
from abc import abstractmethod, ABCMeta # 定义接口 class Connection(metaclass=ABCMeta): @abstractmethod def request(self): pass # 被代理类实现此接口 class RealConnection(Connection): def request(self): print("真实服务端收到请求") # 代理类实现此接口,并进行拓展 class ProxyConnection(Connection): def request(self): print("代理服务端进行额外操作") # 此处构造被代理对象的实例,并继续逻辑操作 RealConnection().request() if __name__ == '__main__': # 客户端调用代理对象,并调用代理对象的接口 p = ProxyConnection() p.request()