Design Pattern¶
decorator, strategy, observer&visitor¶
为什么要把这四个设计模式放在一起来理解?虽然decorator属于“用于组织对象和类的模式”,而其他三个属于“面向任务的模式”,但是它们的应用场景,尤其是问题描述上很类似——同一个场景描述,可能使用不同的设计模式。
组件间的关系强弱程度¶
| 强 | ——-> | ——> | 弱 |
| 组合 | 聚合 | 关联 | 依赖 |
| decorator | strategy | observer | visitor |
observer V.S. visitor¶
- 这两种模式的微妙差异可以从两个场景描述开始:
场景描述1:对于一个Army类我们需要具有如下功能,
- 计算军队的攻击强度(核心任务)
- 把攻击强度的信息转存为文本信息(属于周边任务)
- 计算军队食物消耗
- 军队需要缴纳税金,且组成军队不同单位的税率不同
- …
场景描述2:Login类时保护业务团队财富的大门,
- 市场部门可能会要求将用户登录的IP地址计入日志
- 考虑到安全,运维人员可能会要求用户登录失败时发一封电邮到管理员新乡
- 业务开发团队可能会要求特备关注某个ISP
- …
- 对于这两种场景,第一反应当然是,
- 场景1,既然是类自身需要的功能,当然是都写成类方法啦。
- 场景2,在Login::handleLogin()函数中直接调用其他(团队的)类所开放的方法或者在Login类中直接添加方法就可以啦。
- 但是,面临的问题是相似且显而易见的
- 对于场景1,如果并不知道所有可能需要执行的操作,如果每增加一个操作,就在类中增加一对支持新操作的类属性和方法,类就会变得越来越臃肿。
- 对于场景2,不仅Login::handleLogin()函数会变得冗长,而且Login类会仅仅嵌入到这个特殊的系统中,无法提取出来放置到其他产品中。
- 解决办法
两种模式的有些相同的思路:
- 把一个操作外化为一个类,并且比较p192图11-6和p199 图11-7,会发现外化的这些操作类形成了一个继承体系。
两种模式的细微不同之处在于:
- 场景1,区分“核心属性/方法”和“周边任务”,“核心属性/方法”还是在组件(类)本身实现,“周边任务”包含的属性和方法用visitor class来实现,一个操作就可以外部化为一个visitor类。visitor(observer) interface会为每个visitable(observable) class定义一个visit()方法,而无需为每一个visitor类发展出一套和“被访问类接口”对应的继承体系,见p196 abstract class ArmyVisitor。并且,visitor尤其适合于“每个对象都保存对兄弟节点引用的一组对象”。
- 场景2,observer
- 设计模式本身存在的问题
- visitor
visitor类必将访问“被观察类”的“核心属性/方法”,甚至为了给visitor提供信息,必须给“被观察类接口”提供额外的方法,这就有可能破坏封装。
decorator V.S. observer/visitor¶
- decorator本属于“组织对象和类的模式”,之所以要和两个“面向任务的模式”在一起比较,是因为从场景描述上太相似了
场景描述:真正的web应用在收到请求后,就要对请求进行处理,在执行核心操作前,还要进行如下操作:
- 验证用户请求
- 记录请求
- 将请求中的原始输入处理成某种数据结构
- …
- 最后才是执行核心任务
这一段描述和observer模式对于用户登录后的操作描述极为相似。再加上处理核心任务前的先期任务可能还会增加,又像visitor模式。
- 三种模式在实现方式上的共同点
- 都摒弃了向核心类中添加方法的方式,而是把操作外化为类,甭管这类是自己组建之中还是别的组建(团队)提供的。
- 一个操作外化为一个类,请比较p166图10-4,p192图11-6,和p199 图11-7,会发现外化的操作自身形成了一个继承体系。
- 最关键的区别
是否需要生成一个管道对象,即一个串联起多个操作对象,而不是几个并列的单操作对象,见p169最下面创建的一个过滤器对象。
strategy V.S. decorator¶
在书中虽然把strategy归入“面向任务的模式”,而把“装饰模式”归入“用于组织对象和类的模式”,但是这两种模式中的组件之间都是强“包含”关系,并且两者的场景描述都面临同一个问题——将所有功能建立在继承体系上会导致系统中的类“爆炸式”增多。
- 场景描述
- 场景1:p165-166 图10-3
- 场景2:p183-184 图11-3,11-4
- 两个场景面临的共同问题
将所有功能建立在继承体系上会导致系统中的类“爆炸式”增多。
- 解决思路
两个算法的解决思路类似——把类功能(算法)抽象成独立的class
- 关键区别
- strategy: p184 图11-5 UML类图,strategy class被core class聚合了
- decorator: p169 图10-5 UML类图,core class被decorator class组合了