Design Pattern

decorator, strategy, observer&visitor

为什么要把这四个设计模式放在一起来理解?虽然decorator属于“用于组织对象和类的模式”,而其他三个属于“面向任务的模式”,但是它们的应用场景,尤其是问题描述上很类似——同一个场景描述,可能使用不同的设计模式。

组件间的关系强弱程度

——-> ——>
组合 聚合 关联 依赖
decorator strategy observer visitor

observer V.S. visitor

  1. 这两种模式的微妙差异可以从两个场景描述开始:

场景描述1:对于一个Army类我们需要具有如下功能,

  • 计算军队的攻击强度(核心任务)
  • 把攻击强度的信息转存为文本信息(属于周边任务)
  • 计算军队食物消耗
  • 军队需要缴纳税金,且组成军队不同单位的税率不同

场景描述2:Login类时保护业务团队财富的大门,

  • 市场部门可能会要求将用户登录的IP地址计入日志
  • 考虑到安全,运维人员可能会要求用户登录失败时发一封电邮到管理员新乡
  • 业务开发团队可能会要求特备关注某个ISP
  1. 对于这两种场景,第一反应当然是,
  • 场景1,既然是类自身需要的功能,当然是都写成类方法啦。
  • 场景2,在Login::handleLogin()函数中直接调用其他(团队的)类所开放的方法或者在Login类中直接添加方法就可以啦。
  1. 但是,面临的问题是相似且显而易见的
  • 对于场景1,如果并不知道所有可能需要执行的操作,如果每增加一个操作,就在类中增加一对支持新操作的类属性和方法,类就会变得越来越臃肿。
  • 对于场景2,不仅Login::handleLogin()函数会变得冗长,而且Login类会仅仅嵌入到这个特殊的系统中,无法提取出来放置到其他产品中。
  1. 解决办法

两种模式的有些相同的思路:

  • 把一个操作外化为一个类,并且比较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
  1. 设计模式本身存在的问题
  • visitor

visitor类必将访问“被观察类”的“核心属性/方法”,甚至为了给visitor提供信息,必须给“被观察类接口”提供额外的方法,这就有可能破坏封装。

decorator V.S. observer/visitor

  1. decorator本属于“组织对象和类的模式”,之所以要和两个“面向任务的模式”在一起比较,是因为从场景描述上太相似了

场景描述:真正的web应用在收到请求后,就要对请求进行处理,在执行核心操作前,还要进行如下操作:

  • 验证用户请求
  • 记录请求
  • 将请求中的原始输入处理成某种数据结构
  • 最后才是执行核心任务

这一段描述和observer模式对于用户登录后的操作描述极为相似。再加上处理核心任务前的先期任务可能还会增加,又像visitor模式。

  1. 三种模式在实现方式上的共同点
  • 都摒弃了向核心类中添加方法的方式,而是把操作外化为类,甭管这类是自己组建之中还是别的组建(团队)提供的。
  • 一个操作外化为一个类,请比较p166图10-4,p192图11-6,和p199 图11-7,会发现外化的操作自身形成了一个继承体系。
  1. 最关键的区别

是否需要生成一个管道对象,即一个串联起多个操作对象,而不是几个并列的单操作对象,见p169最下面创建的一个过滤器对象。

strategy V.S. decorator

在书中虽然把strategy归入“面向任务的模式”,而把“装饰模式”归入“用于组织对象和类的模式”,但是这两种模式中的组件之间都是强“包含”关系,并且两者的场景描述都面临同一个问题——将所有功能建立在继承体系上会导致系统中的类“爆炸式”增多。

  1. 场景描述
  • 场景1:p165-166 图10-3
  • 场景2:p183-184 图11-3,11-4
  1. 两个场景面临的共同问题

将所有功能建立在继承体系上会导致系统中的类“爆炸式”增多。

  1. 解决思路

两个算法的解决思路类似——把类功能(算法)抽象成独立的class

  1. 关键区别
  • strategy: p184 图11-5 UML类图,strategy class被core class聚合了
  • decorator: p169 图10-5 UML类图,core class被decorator class组合了