行为
即动态、关注点在于对象之间动态(通信、交互)的部分
前言
请先确保能读懂UML类图。UML类图基础 可参考
涉及到的设计模式
职责链模式 - chain of responsibility pattern
- 应用场景:常用于事件模型,这种情况下不变的是事件的发生,变化的是事件发生后在不同时空间的不同处理流程(分离变化)
- 举例:
- web框架的middleware
- 优势:
- 处理链可以动态变动
- 容易对外提供扩展点加入到处理链
- UML图(来源《冒号课堂》)
- 备注:虽然上图中的Handler是抽象类(斜体的),但是实际用Interface也是完全可以的、具体使用什么就看你的应用中的具体需求了
- 体现的设计原则:
- 单一职责原则:否则一次处理的代码集合了各种职责
- 最少知识原则:每个处理点只关心自己
- 对比装饰者模式:职责模式关注行为上的职责分解、装饰者模式关注结构上的职责结合(但是职责模式也可实现装饰者模式的效果、即每个点正好就是对某对象进行装饰)
命令模式 - command pattern
- 应用场景:在出于特定目的,需要控制命令的执行者/执行时机的场景(e.g: 游戏用户的此局游戏数据的记录、等到用户全部完成后再触发各项纪录“入库”)
- 实现核心:封装需要执行的命令及其上下文成为一个“命令对象”,而后在特定的时机由触发者触发此命令(回调)的执行。从而实现了命令的接收与执行在时间和空间上的解耦
- UML图(来源《冒号课堂》):
- 在Receiver与Invoker之间加入了间接的Command对象、如Invoker可以不断的取出特定数量的Command对象并调用其execute()方法即可(不需要关心其是什么、需要什么,因为这些它都自己封装好了)
- 这里还需要注意的一点是对Receiver这个对象的理解,表示的是“执行对象”。client接收到命令后找到实际的执行者(receiver)、和执行上下文一起封装为一个command对象(如执行者是个函数、执行上下文是其参数)
- 体现的设计原则
- 间接原则&保变原则:通过Command这个间接对象、保护了command的执行者、执行时间的变化性。从而可以是不同的client构造了不同的receiver以及context、但是执行的接口是统一的、invoker之间调用执行即可
观察者/发布订阅模式 - observer pattern
- 应用场景:应对一对多的情况、当一个对象接收到消息/状态发生改变后、多个平等的依赖者对象都需要收到消息。也是常用于事件驱动的编程。
- 举例:用户登录后、发欢迎邮件、初始化账户默认信息等等依赖对象都收到消息然后自己进行对应处理
- UML图(来源《冒号课堂》):
- 优势:解耦原本是同级别的依赖对象(observer)。对比下如果只有一个消息接收入口、那么此入口需要完成多个对象的状态更新调用,而通过消息的多方传播,每个对象都只需要对自己负责即可
- 体现的设计原则
- 好莱坞原则:don‘t call us, we‘ll call you
- 控制反转:此时subject是底层、而observer是高层。如MVC模型中的底层(模型 - model)与高层(视图 - view)之间
中介模式 - mediator pattern
- 设计核心:利用纯虚构原则造出一个信息交换中心,改变一群对象之间的信息交流为必须要知道具体目的对象为只需要知道信息交换中心即可
- 应用举例:其实DNS系统就是这样的、其作为域名到ip的映射、如果没有中心服务器,总不能每台个人电脑都保存一个完备的映射表吧,那样对于存储以及更新的问题都太麻烦了
- UML类图(来源《冒号课堂》):
- 对比观察者模式:
- 相比观察者模式的具体对象是功能单一的观察者(单向)、中介模式的对象可能即是观察者又是发布者(双向),且相互之间的观察/发布关系复杂
- 中介者会形成一个上帝视角的地方、从而可以做到如编排顺序等等操作、观察者模式是做不到的
- 体现的设计原则
- 局部化原则 & 关注点分离原则:将多有的通信关系/逻辑集中起来
- 迪米特法则/最少知识原则:发布者不需要知道具体的接收者、只是发布到中介即可。(特别想象下、在分布式情况下、若某发布者的接收者发生了变化、使用中介模式的话更新起来会特别的稳定/方便!)
状态模式 - state pattern
- 设计核心:一个对象可能有多种状态,且在不同的状态下的行为是不同的。而引起状态的变化需要事件(event)的发生、与此同时可能会伴随着别的一些操作(action)[额外操作不是一定有的、也有可能事件发生只改变状态]
- 应用举例:同样的一个执行操作,对于不同身份的user表现不同,如果是superuser就直接执行、如果是普通用户就提示会进入审批、如果是未登录用户直接拒绝
- UML类图(来源《冒号课堂》):
- 由上图可见、核心实现是:
- 单独维护状态类
- 不同状态的行为通过委托到具体的状态类来执行
- 体现的设计原则
- 保变原则:将与state相关的打包独立出为单独的对象
备忘录模式 - memento pattern
- 应用:用于常需要恢复之前某个状态的程序
- UML类图(来源《冒号课堂》):
访问者模式 - visitor pattern
- 预知识
- 关于确定一个方法调用具体位置的情况
- 如果方法名是唯一的,没有异议,直接能确定
- 如果方法名不唯一,通常由“参数类型” + “参数个数”来确认
- 若分派在编译期决定、称“静态分派”
- 若分派在运行期决定、称“动态分派”(子类型多态的实现原理)
- 备注:python和go都是原生不支持函数重载
- 单分派
- 在运行期,根据方法的接收者(即所属对象)来决定调用的情况【单指的就是只参考了所属对象这一个参数】
- 大多数常见语言都是此机制(Python、C++、Java…)
- 双分派
- 根据 方法的接收者(即所属对象) + 参数的数量以及类型 共同决定调用
- 少数语言支持(Clojure、Groovy、Dylan)
- 关于确定一个方法调用具体位置的情况
- TODO、由于python和go都不支持函数重载,用的少、暂时掠过
迭代器模式 - iterator pattern
将遍历单独提出来成为一个Interface,从而分离了对象与对象的遍历,带来的优势是
- 可以方便的切换具体迭代的方式/算法而不修改对象本身
- 针对特定的数据结构、语言可预先写好一些高性能/基础的遍历方式供用户使用
- 基于此Interface、方便用户开发自己的遍历算法并使用
核心:赋予了用户灵活选择/自定义对容器的遍历方式
UML类图(来源《冒号课堂》)
调用举例:
aggregate = ConcreteAggregate()
// … 填充元素
iterator = aggregate.createIterator(ConcrteIterator) // 若要不同的遍历方式/限制只需换对应的ConcrteIterator即可
while iterator.next() {
//….}
解释器模式 - interpreter pattern
- 应用:为某个语言定义其语法表示,并且写出一个解释器用于处理此语法
- UML类图(来源《冒号课堂》)
参考
《冒号课堂》第13章第3节