目录
- 一、观察者模式
- 1.1 概述
- 1.2 类图
- 1.3 实现
- 二、迭代器模式
- 2.1 概述
- 2.2 类图
- 三、责任链模式
- 3.1 概述
- 3.2 类图
- 3.2 实现
- 四、模板方法
- 4.1 概述
- 4.2 类图
- 4.3 实现
- 五、策略模式
- 5.1 概述
- 5.2 类图
- 5.3 实现
- 六、状态模式
- 6.1 概述
- 6.2 类图
- 6.3 实现
- 七、中介模式
- 7.1 概述
- 7.2 类图
- 7.3 实现
- 八、命令模式
- 8.1 概述
- 8.2 类图
- 8.3 实现
- 九、备忘录模式
- 9.1 概述
- 9.2 类图
- 9.3 实现
- 十、访问者模式
- 10.1 概述
- 10.2 类图
- 10.3 实现
一、观察者模式
1.1 概述
- 定义:在对象间定义一种一对多的依赖关系,当这个对象状态发生改变时,所有依赖它的对象都会被通知并自动更新,又叫发布/订阅(Publish/Subscribe)模式、模型/视图(Model/View)模式、监听模式
- 适用场景:一个对象更新,需要其他对象同步更新
- 设计要点:
- 角色判定:首先需要明确被观察者(发布者)和观察者(订阅者),一般二者是一对多的关系
- 核心方法:
- 订阅者:接口update(self,observer,object),实现推拉模型
- 推模型(常用):数据从发布者推到订阅者,推送的信息全部通过object传递
- 拉模型:数据流向同,发布者将自己作为对象放入observer传给订阅者,订阅者可以以此调用发布者的方法或数据
- 发布者:notifyObservers是自动化沟通发布者和订阅者的核心方法
1.2 类图
- 角色:
- 被观察者(observable):被监听的对象
- 观察者(observe):监听的对象
1.3 实现
- 代码示例
from abc import ABCMeta, abstractmethod
class Observable:
"""发布者基类"""
def __init__(self):
# 存储订阅者对象的列表
self.__observers = []
# 增加订阅者:必须方法,observer为订阅者类对象
def add_observer(self, observer):
self.__observers.append(observer)
# 删除订阅者:必须方法,observer为订阅者类对象
def remove_observer(self, observer):
self.__observers.remove(observer)
# 通知订阅者:同时调用订阅者类对象的方法update
# 推信息:发布者向订阅者传递参数,通过msg传给obj的update
# 拉信息:发布者将自身self,作为对象传给订阅者update
def notify_observers(self, msg=0):
for obj in self.__observers:
obj.update(self, msg)
class Account(Observable):
"""发布者具体实现:用户账户"""
def __init__(self):
super().__init__()
= "用户账户"
# 此处调用了发布者基类的通知方法notifyObservers
def login(self, name, ip):
# 如果登陆验证不在白名单里,就出发通知方法
if not self.__is_auth(ip):
# 类方法默认第一个参数是self
self.notify_observers({"name": name, "ip": ip})
def __is_auth(self, ip):
# ip白名单
ip_white_list = [
"101.47.18.9"
]
# 如果在白名单里返回true
return ip in ip_white_list
class Observer(metaclass=ABCMeta):
"""订阅者基类"""
# 虚函数:此类的派生类必须重写此函数
# 拉信息:由订阅者主动从发布者类对象observable中提取
# 推信息(常用):由订阅者被动的从msg中拿数据
@abstractmethod
def update(self, observable, msg):
pass
class SmsSender(Observer):
"""订阅者实现:短信发送器"""
def update(self, observable, msg):
print("推模型:[短信发送] " + msg["name"] + "异常登陆IP:" + msg["ip"])
print("拉模型:拿到发布者的属性为", observable.name)
class MailSender(Observer):
"""订阅者实现:邮件发送器"""
def update(self, observable, msg):
print("[邮件发送] " + msg["name"] + "异常登陆IP:" + msg["ip"])
if __name__ == "__main__":
# 创建发布者
account = Account()
# 向发布者添加订阅者对象:短信
account.add_observer(SmsSender())
# 向发布者添加订阅者对象:电子邮件
account.add_observer(MailSender())
# 发布者状态发生变化
# 在白名单里
account.login("Sunny", "101.47.18.9")
# 不在白名单里
account.login("Tony", "67.218.147.69")
###################################################
>>> 推模型:[短信发送] Tony异常登陆IP:67.218.147.69
>>> 拉模型:拿到发布者的属性为 用户账户
>>> [邮件发送] Tony异常登陆IP:67.218.147.69
二、迭代器模式
2.1 概述
- 定义:它可以让用户透过特定的接口巡访容器中每个元素而不用了解底层实现
- 设计要点:了解容器的数据结构及可能的层次结构
- 适用场景:需要提供统一的访问接口,从而对不同的集合使用统一的算法
2.2 类图
- 实现:传送门(第二章)
三、责任链模式
3.1 概述
- 定义:为避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求。将这些接收对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止
- 适用场景:审批流程(账务报销、转岗申请等)
- 动态对象:有多个对象可以处理同一个请求,具体哪个对象处理该请求在运行时刻自动确定
- 层级传递:请求的处理具有明显的一层层传递关系
- 过程动态:请求的处理流程和顺序需要程序运行时动态确定
- 设计要点:
- 责任人抽象:责任人共同特征即都可以处理请求,对责任人进行抽象关注请求处理方法;
- 循环调用:明确请求在责任链中流转,防止循环调用
3.2 类图
- 角色
- 请求的发送者(Requester ):Request 是请求的包装类,封装一个请求对象
- 责任人的抽象基类(Responsible):也是责任链的节点
- 具体的责任人:ResponsiblePersonA、ResponsiblePersonB
3.2 实现
- 代码示例
from abc import ABCMeta, abstractmethod
class Handler(metaclass=ABCMeta):
@abstractmethod
def handler_leave(self, day):
pass
# 责任链的终点:以下都是责任链上的节点
class GeneralManager(Handler):
def handler_leave(self, day):
if day < 10:
print(f"总经理:批准假{day}天")
else:
print("总经理:不批")
# 扩展:以此类为模板,可以扩展
class DepartmentMananger(Handler):
def __init__(self):
self.next = GeneralManager()
def handler_leave(self, day):
if day <= 5:
print(f"部门经理:批准假{day}天")
else:
# 此语句时建立责任链的纽带,数据结构的链式思维
print("部门经理:权限不足")
self.next.handler_leave(day)
class ProjectDirector(Handler):
def __init__(self):
self.next = DepartmentMananger()
def handler_leave(self, day):
if day <= 3:
print(f"项目主管:批准假{day}天")
else:
# 此语句时建立责任链的纽带,数据结构的链式思维
print("项目主管:权限不足")
self.next.handler_leave(day)
if __name__ == "__main__":
# 客户端
h = ProjectDirector()
print("请假2天时")
h.handler_leave(2)
print("请假15天时")
h.handler_leave(15)
#####################################################
>>> 请假2天时
>>> 项目主管:批准假2天
>>> 请假15天时
>>> 项目主管:权限不足
>>> 部门经理:权限不足
>>> 总经理:不批
四、模板方法
4.1 概述
- 定义:模板方法模式定义了一个算法的步骤,并允许子类为其提供实践方式,让子类别在不改变算法架构的情况下,重新定义算法中的某些步骤
- 设计要点:简单的利用了面向对象的继承机制
- 适用场景:
- 不变与可变:一次性实现一个算法的不变部分,并将可变的行为留给子类来实现
- 重构:各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复
4.2 类图
- 角色
- 抽象类:定义抽象的原子操作(钩子操作),实现一个模板方法作为算法的骨架
- 具体类:实现框架中的一些特定步骤
4.3 实现
- 代码示例
from abc import ABCMeta, abstractmethod
# 模版方法的抽象类:定义了三个插槽方法,等派生类重写
# 定义了一个方法,作为骨架
class Window(metaclass=ABCMeta):
@abstractmethod
def start(self):
pass
@abstractmethod
def repaint(self):
pass
@abstractmethod
def stop(self):
pass
# 骨架方法:模版方法的核心
def run(self):
self.start()
self.repaint()
self.stop()
# 派生类:实现了三个方法,骨架方法在基类中
class MyWindow(Window):
def __init__(self,msg):
self.msg=msg
def start(self):
print("窗口开始运行...")
def stop(self):
print("窗口停止运行...")
def repaint(self):
print(self.msg)
if __name__ == "__main__":
MyWindow("这是窗口信息").run()
#################################
>>> 窗口开始运行...
>>> 这是窗口信息
>>> 窗口停止运行...
五、策略模式
5.1 概述
- 定义:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换,使算法可以独立于使用它的用户而变化
- 设计要点:设计弊端为所有策略类都需要对外暴露
- 适用场景:
- 行为区分:系统中多个类之间的区别仅在于有不同的行为
- 动态选择:一个系统需要动态地在几种算法中选择一种
5.2 类图
- 角色
- 上下文环境(Context):起着承上启下的封装作用,屏蔽上层应用对策略(算法)的直接访问,封装可能存在的变化
- 策略的抽象(Strategy):策略(算法)的抽象类,定义统一的接口,规定每个子类必须实现的方法
- 具备的策略:策略的具体实现者,可以有多个不同的(算法或规则)实现
5.3 实现
- 代码示例
from abc import ABCMeta, abstractmethod
# 类定义
class Person:
def __init__(self, name, weight, height):
= name
self.weight = weight
self.height = height
# 定义策略接口:抽象接口类
class Icompare(metaclass=ABCMeta):
@abstractmethod
def compare(self, person1, person2):
pass
# 体重比较策略实现:接口输入和返回的数据类型必须相同
class WeightCompare(Icompare):
def compare(self, person1, person2):
return person1.weight > person2.weight
# 身高比较策略实现:接口输入和返回的数据类型必须相同
class HeightCompare(Icompare):
def compare(self, person1, person2):
return person1.height > person2.height
# 上下文:客户端调用,初始化传入策略对象,
# 对应方法,返回以此策略的结果
class Context:
def __init__(self, strategy):
self.__strategy = strategy
def get_res(self, person1, person2):
return self.__strategy.compare(person1, person2)
if __name__ == "__main__":
xiaoming = Person("xiaoming", 70, 177)
panghu = Person("panghu", 80, 166)
# 客户端选择以哪个算法为比较策略:1、以重量 2、以身高
context = Context(WeightCompare())
# context = Context(HeightCompare())
if context.get_res(xiaoming, panghu):
print("前者比后者大为真")
else:
print("前者比后者大为假")
六、状态模式
6.1 概述
- 定义:允许一个对象在其内部状态发生改变时改变其行为,使这个对象看上去就像改变了它的类型一样
- 适用场景:一个事物(对象)有多种状态,在不同的状态下所表现出来的行为和属性不一样
- 设计要点
- 单例:每一种状态应当只有唯一的实例,需要单例模式
- 状态匹配方法:状态有时候会非常复杂,可以把决定性属性抽象成一个类 StateInfo,判断状态属性是否符合当前的状态isMatch时就可以传入更多的信息
6.2 类图
- 角色
- 上下文环境类(Context):负责在系统中记录状态,以及调度状态的转移
- 抽象状态类(State):定义抽象状态对应的行为
- 具体状态类(ConcreteState):定义具体状态对应的具体行为,类图中StateA、StateB
6.3 实现
- 代码示例
from abc import ABCMeta, abstractmethod
# 抽象状态类
class State(metaclass=ABCMeta):
@abstractmethod
def rising_temperature(self):
pass
def lowing_temperature(self):
pass
# 具体状态类:以下三个
# 冰块
class SolidState(State):
def rising_temperature(self):
print("温度正在升高...变成液态")
# 链式返回:指定下一个状态的对象
return LiquidState()
def lowing_temperature(self):
print("温度正在降低...已为固态,状态不变")
# 已为终点:返回自己
return self
# 液态水
class LiquidState(State):
def rising_temperature(self):
print("温度正在升高...变成气态")
# 链式返回:指定下一个状态的对象
return GaseousState()
def lowing_temperature(self):
print("温度正在降低...变成固态")
# 链式返回:指定下一个状态的对象
return SolidState()
# 水蒸气
class GaseousState(State):
def rising_temperature(self):
print("温度正在升高...已为气态,状态不变")
# 状态终点:返回自己
return self
def lowing_temperature(self):
print("温度正在降低...变成液态")
# 链式返回:指定下一个状态的对象
return LiquidState()
# 上下文:用来调度状态改变
class Context:
__state = None
# state为类State的派生类对象
def set_state(self, state):
self.__state = state
def rising_temperature(self):
# 类State的派生类对象方法rising_temperature
self.set_state(self.__state.rising_temperature())
def lowing_temperature(self):
# 类State的派生类对象方法lowing_temperature
self.set_state(self.__state.lowing_temperature())
if __name__ == "__main__":
water = Context()
# 先设定状态为气态
water.set_state(GaseousState())
water.rising_temperature()
water.lowing_temperature()
water.lowing_temperature()
water.lowing_temperature()
###############################################
>>> 温度正在升高...已为气态,状态不变
>>> 温度正在降低...变成液态
>>> 温度正在降低...变成固态
>>> 温度正在降低...已为固态,状态不变
七、中介模式
7.1 概述
- 定义:用一个对象来封装一系列的对象交互,中介者使各对象不需要显示地相互引用,从而使耦合松散,而且可以独立地改变它们之间的交互
- 适用场景:在很多系统中,多个类交互很容易相互耦合,此模式即将多对多的交互关系,转换为一对多的交互关系
- 设计要点:复杂度,交互的复杂度转变成了中介者的复杂度,中介者类会变得越来越庞大和复杂
7.2 类图
- 角色
- 交互对象(InteractiveObject):要进行交互的一系列对象,可为无关的多个类对象,也可为继承关系的相似类
- 中介者(Mediator):负责协调各个对象之间的交互
- 具体中介者(Mediator):中介的具体实现
7.3 实现
- 代码示例
# 以下均为具体实现类,之间不互相通信
# 而与中介类一对一通信
class Consumer:
def buy(self):
print("消费者购买产品")
class Producer:
def produce(self):
print("生产者生产产品")
class Mediator:
# 用于保存具体实现类的实例
def __init__(self):
self.consumer = None
self.producer = None
# 此处为核心,各个类之间在此处彼此交互,
# 此处以后也会逐渐膨胀
def deal(self):
self.producer.produce()
self.consumer.buy()
if __name__ == '__main__':
# 创建中介者,并设置两个属性
mediator = Mediator()
mediator.consumer = Consumer()
mediator.producer = Producer()
# 客户端不用关心两个属性的交互过程,被中介者封装了
mediator.deal()
八、命令模式
8.1 概述
- 定义:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能
- 设计要点:系统中可能有很多命令,而每一命令都要一个具体的类去封装,容易使命令的类急剧膨胀
- 适用场景:GUI按钮设计,宏命令(一系列操作命令组合),需要实现撤销命令操作
8.2 类图
- 角色
- 调度者(Invoker):接收任务并发送命令,对接用户的需求并执行内部的命令,负责外部用户与内部命令的交互
- 命令接口(Command):表示一项任务或一个动作,是所有命令的抽象类,定义了统一的执行方法execute
- 具体的命令实现类(Receiver):包装了命令的接收者,控制传入的接收者和具体命令
- 用户(Client):命令的使用者,即真正的用户
8.3 实现
- 代码示例
from abc import ABCMeta, abstractmethod
import os
import time
########################################################
# 底层接收者:抽象业务实现类
class ABCReceiver(metaclass=ABCMeta):
@abstractmethod
def create_file(self, filename):
pass
# 回退操作:删除操作固定,所以写死在抽象接收者里
def del_file(self, filename):
os.remove(filename)
# 具体业务实现类:接收命令对象传来的参数,其为扩展点,可以扩展具体如何创建
class FileReceiver(ABCReceiver):
def create_file(self, filename):
with open(filename, 'w')as f:
pass
########################################################
# 中层命令接口:核心,调用与实现的中间层,解耦,接收传入的接收者对象和参数
class AbcCommand(metaclass=ABCMeta):
@abstractmethod
def execute(self):
pass
# 回退操作
@abstractmethod
def cancel(self):
pass
# 中层具体命令:调用具体接收者实现功能
class FileCommand(AbcCommand):
def __init__(self, receiver, new_file_name):
self.receiver = receiver
self.new_file_name = new_file_name
def execute(self):
self.receiver.create_file(self.new_file_name)
# 回退操作:多命令是先回复最后执行命令并依次向前
def cancel(self):
self.receiver.del_file(self.new_file_name)
########################################################
# 顶层调用者:所有操作都被包含在调用者对象中,生成一次对象,后续运行
# 这一组封装命令只需调用run方法即可
# 原子化操作:操作要么所有命令都发生,要么全都不发生(出现问题回滚命令)
class FileInvoker:
def __init__(self, in_command):
self.command = in_command
def run(self):
self.command.execute()
# 回退操作
def cannel(self):
self.command.cancel()
########################################################
if __name__ == '__main__':
# 接收者对象:此处可以选择不同接收者,以选择不同命令组合
new_file_receiver = FileReceiver()
# 命令对象:传入接收者对象和参数
command = FileCommand(new_file_receiver, 'data.txt')
# 构造调用者:传入命令对象
invoker = FileInvoker(command)
# 执行命令
invoker.run()
# 暂停三秒
time.sleep(3)
# 现在又想撤销
invoker.cannel()
# 暂停三秒
time.sleep(3)
# 又想重复执行
invoker.run()
九、备忘录模式
9.1 概述
- 定义:在不破坏内部结构的前提下捕获一个对象的内部状态,这样便可在以后将该对象恢复到原先保存的状态
- 设计要点:如果类的成员变量过多,势必会占用比较多的资源,可限制保存次数以减少消耗的内存
- 适用场景:
- 保存/恢复对象的状态:如游戏的存档、虚拟机的快照
- 撤销、恢复功能的场景:如Word撤销、DOS命令行、Linux终端的命令记忆功能
- 回滚:提供一个可回滚的操作,如数据库的事务管理
9.2 类图
- 角色:
- 发起人(Originator):需要进行备份的对象
- 备忘录(Memento):备份的状态,即一个备份的存档,在恢复备忘数据的时候提供发起人需要的状态
- 管理员(Caretaker):备份存档的管理者,由它负责与发起人的交互
9.3 实现
- 代码实现
# 发起者:至少需要实现save和restore两个方法
class Machine:
def __init__(self, context):
self._state = context
def get_context(self):
print("当前状态为:", self._state)
def set_context(self, context):
self._state = context
# 此处需要手动创建需要保存的字典,传给备忘录对象
def save(self):
print("保存中...保存完毕")
return Memento({'state': self._state})
# 从备忘录中拿到状态并提取其中的值覆盖发起者的属性
def restore(self, memento):
print("读取中...读取完毕")
self._state = memento.state['state']
# 备忘录类:只包含状态属性
class Memento:
def __init__(self, state):
self.state = state
# 备忘录管理员类:负责储存历次save的备忘录
class Caretaker:
def __init__(self, machine):
# 备忘录保存池
self._mementos = []
# 为了self.save方法调用发起者save方法
self._originator = machine
# 将发起者生成的备忘录类实例保存在本备忘录保存池中
def save(self):
self._mementos.append(self._originator.save())
# 从备忘录池后面取备忘录给发起者进行恢复
def restore(self):
# 加了判断:如果空,直接返回None
if not len(self._mementos):
print("备忘录中已无备份")
return
memento = self._mementos.pop()
self._originator.restore(memento)
if __name__ == "__main__":
# 客户端:生成发起类对象、备忘录管理对象
originator = Machine("初始化状态")
caretaker = Caretaker(originator)
# 隐式生成备忘录对象及添加到备忘录管理对象的备忘录池中
caretaker.save()
originator.set_context("第一次修改")
caretaker.save()
caretaker.restore()
originator.get_context()
caretaker.restore()
originator.get_context()
print(20*"#")
caretaker.restore()
originator.get_context()
############################################
>>> 保存中...保存完毕
>>> 保存中...保存完毕
>>> 读取中...读取完毕
>>> 当前状态为: 第一次修改
>>> 读取中...读取完毕
>>> 当前状态为: 初始化状态
>>> ####################
>>> 备忘录中已无备份
>>> 当前状态为: 初始化状态
十、访问者模式
10.1 概述
- 定义:封装一些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作
- 设计要点:模式中,每增加一个新的元素类都要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,这违背了“开闭-原则”的要求
- 适用场景:
- 结构单一操作扩展:对象结构简单,很少改变,但经常需要在此对象结构上定义新的操作
- 操作特性:需要对一个对象结构中的对象进行很多不同的并且不相关的操作,需要避免让这些操作“污染”这些对象的类
10.2 类图
- 角色:
- 访问者(Visitor):负责对数据节点进行访问和操作
- 数据节点(Visitor):即要被操作的数据对象
- 对象结构(Visitor):数据结构的管理类,也是数据对象的一个容器,可遍历容器内的所有元素
10.3 实现
- 代码示例
from abc import abstractmethod, ABCMeta
######################################################
# 数据节点:数据对象
class Product(metaclass=ABCMeta):
# 此处为数据:特点是已经稳定,不会增加或减少
def __init__(self, name, price):
= name
self.price = price
# 核心方法:传入访问者对象
@abstractmethod
def accept(self, visitor):
pass
# accept方法会调用访问者对象的visit方法
class Food(Product):
def accept(self, visitor):
visitor.visit(self)
class Drink(Product):
def accept(self, visitor):
visitor.visit(self)
######################################################
# 访问者对象:依赖于数据节点对象
class Visitor(metaclass=ABCMeta):
# 核心方法:传入数据节点对象
@abstractmethod
def visit(self, product):
pass
class Seller:
# 此处接收数据节点对象,并提取其中的数据,通常是读操作
def visit(self, product):
print("出售者查看了产品的价格:", product.price)
class Buyer:
# 两个访问者会进行不同的数据处理,不会影响原数据节点对象
def visit(self, product):
print("购买者查看了产品的名称:", product.name)
######################################################
# 对象结构:用于存储数据节点对象的数据池
class ProductPile:
# 存储生成的数据节点对象们
def __init__(self):
self.products = []
# 向数据池中增加对象
def add_product(self, obj):
self.products.append(obj)
def remove_products(self, obj):
self.products.remove(obj)
# 数据池遍历每个数据节点并调用 其accept方法
# 进而调用其中 访问者的visit方法
def visit(self, visitor):
for obj in self.products:
obj.accept(visitor)
if __name__ == '__main__':
# 创建节点池,并新增节点
product_pool = ProductPile()
product_pool.add_product(Food("面包", 5))
product_pool.add_product(Food("火腿肠", 2))
product_pool.add_product(Drink("矿泉水", 2))
# 创建访问者角色并传给数据池
seller = Seller()
buyer = Buyer()
product_pool.visit(seller)
product_pool.visit(buyer)
######################################################
>>> 出售者查看了产品的价格: 5
>>> 出售者查看了产品的价格: 2
>>> 出售者查看了产品的价格: 2
>>> 购买者查看了产品的名称: 面包
>>> 购买者查看了产品的名称: 火腿肠
>>> 购买者查看了产品的名称: 矿泉水