接前文:Python与设计模式(二):结构型模式(下)
行为型模式主要处理对象间的通信问题,包括责任链模式、命令模式、解释器模式、迭代器模式、观察者模式、状态模式、策略模式、模板模式等。
1. 责任链模式
责任链模式一般用于任务的链式处理。前一个处理者处理后,任务传递给下一个处理者,直至完成。
大家常用的例子是OA系统中的审批流程,比如请假。假设3天以下的假期只需要团队长审批,3天以上的同时需要部门经理审批,7天以上的还需要业务总监审批,那么,就形成了一条请假审批的责任链:主管审批之后,任务没有完成,提交至经理,如果还没有完成,则继续往上级提交。
一般的代码会为每一个审批级构建一个类,定义它的处理方法和下一个处理者:
class Director: successor = None def handle_vocation(self, days): passclass Manager: successor = Director() def handle_vocation(self, days): if days < 7: pass else: self.successor.handle_vocation(days)class Teamleader: successor = Manager() def handle_vocation(self, days): if days < 3: pass else: self.successor.handle_vocation(days)
作为例子,我们可以通过这个代码大概理解责任链模式的含义。通过责任链模式,我们解耦了请求发起者和请求处理者之间的关系,请假的人不需要关心谁来审批的问题,只需要把假条给他的直接主管。
但我们都知道,现实中应该不会这么写代码。今天审批请假,明天审批出差,后天审批报销,不同层级的人走的审批路线还不一样,这样的类和方法是写不完的,缺乏灵活性。
我们看钉钉的设计,可以由行政部门根据需要,动态设计审批流程,不同岗位、不同部门、不同层级的员工对应不同的流程,这才是比较贴合实际的情形,我们一般也会这么写。
class Process: id = 0 name = '' class Step: id = 0 name = '' process_id = 0 pre_step_id = 0 next_step_id = 0
即有一个流程对象,每个流程对象有不同的步骤,每个步骤有前置步骤和后续步骤,这里使用id来表示,其实是对应着实际中的数据库记录。可以每完成一个步骤,就生成下一个步骤,也可以在任务一开始就生成所有步骤。
仔细想想,这应该只是处理实际需求中的责任链模式,而不是在代码的组织中体现了责任链模式,可供参考吧。毕竟,需要硬编码写死的责任链还是比较少的。
2. 命令模式
命令模式的目的也是解耦请求发起者和请求处理者之间的关系。方式是把请求(或者说命令)做单独的封装,通过请求的执行方法来调用处理者完成任务。
常用的例子是餐馆点餐:不同的厨师负责不同的菜品,服务员需要根据客户点的菜,将请求发给不同的厨师。通过命令模式,服务员不需要知道哪个厨师负责哪个菜品,相关信息保存在对应菜品(即请求,或者说命令)的信息中,调用执行方法时,将自动通知对应的厨师完成菜品。
因为请求单独做了封装,所以可以保存在一个队列之类的容器中,也可以随时撤销。
有些不需要马上完成的工作,打包成命令,放入某个队列,等处理者有空的时候再执行是很合理的。比如一些服务器维护、数据库备份命令等,我们一般设置定时任务来进行处理,有时会设置在某个预计访问量比较少的时间段,但这个时间段并不总是可靠的,那么,就可以先放入任务队列中。如果CPU占用率、硬盘读写量低于设定值,就让命令执行,否则就放回队列继续等待。
在Python中,一个简单示例如下(来自网页:https://sourcemaking.com/design_patterns/command/python/1):
import abcclass Requester: def __init__(self): self._commands = [] def add_command(self, command): self._commands.append(command) def send_commands(self): for command in self._commands: command.execute()class Command(metaclass=abc.ABCMeta): def __init__(self, receiver): self._receiver = receiver @abc.abstractmethod def execute(self): passclass Command_1(Command): def execute(self): sefl._receiver.action()class Receiver: def action(self): passdef main(): receiver = Receiver() command_1 = Command_1() requester = Requester() requester.add_command(command_1) requester.send_commands()if __name__ == "__main__": main()
3. 观察者模式
与责任链模式、命令模式一样,观察者模式也用于处理消息发送者和接收者之间的关系。
不同的是:
- 责任链模式下,发送者只需要知道第一个消息接收者就行,消息在一个个接收者之间链式传播;
- 命令模式下,消息发送者只需要了解有什么命令,至于命令要传给谁执行,则由命令自身决定;
- 而在观察者模式下,消息发送者把接收者保存在一个列表中,当自身状态发生改变,就通知列表中的所有成员。
Python 主流 Web 框架 Django 有一个第三方包,django-observer,就提供了跟踪模型字段变化的简洁方式,用户可以注册回调函数,在字段变化时自动执行一些任务。
被观察者通知观察者,可以同步通知,即直接调用一些函数;也可以通过消息队列进行异步通知,这样,被观察者就不用等待观察者的反馈,直接进行自己的下一步任务。
就通知消息来说,可以将准确的变动信息直接告知接收者,即所谓的“push”模式,也可以只告知发生变动,让消息接收者根据需要拉取信息,即所谓的“pull”模式。
Python中的观察者模式没什么特殊的,但是可以通过属性设置器在类属性变动时触发通知动作。简单示例如下:
class Notifier: def __init__(self): self._observers = set() self._state = None def add_observer(self, observer): self._observers.add(observer) def remove_boserver(self, observer): self._observers.discard(observer) def _notify(self): for observer in self._observers: observer.update(self.state) @property def state(self): return self._state @state.setter def state(self, obj_state): self._state = obj_state self._notify()
4. 状态模式
有限状态机是现实中常见的场景:当对象处于不同状态时,执行不同的行为;或者当对象的状态发生变化时,执行特定的行为。
常见的例子是零食贩卖机:当我们塞入零钱时,达到某些零食的价格,可能对应零食的指示灯就会变绿,在这种状态下,我们可以正常购买,而有些零食可能价格较贵,指示灯还是红色的,点击购买时会给出余额不足的提示。
在代码中,实现状态模式,一般要关注两点,一个是状态变化时需要执行的动作,一个是处在不同状态时,对象会表现出不同的行为。
简单代码体现如下:
class VenderMachine: def set_state(self, state): do_something() self._state = state def execute(self): self._state.execute()class State_A: def execute(self): passclass State_B: def execute(self): passvender_machine = VenderMachine()state_a = State_A()vender_machine.set_state(state_a)vender_machine.execute()
在Python中,很多第三方库提供了状态机的简单实现,目前,GitHub 上 Star 最多的应该是 transition 模块(https://github.com/pytransitions/transitions),有兴趣可以直接参考文档,还是挺有意思的。
未完待续……