对象的责任类似于Oozinoz公司呼叫中心服务代表的责任。当我们拨打Oozinoz公司的服务电话订购焰火制品的时候,接听电话的人员就是公司的代表---即代理。他所要执行的任务都是既定的,即将客户发起的任务委托给其他系统或人员去执行。有时候,服务代表会将用户的请求提交给唯一的核心机关,由它来调解纠纷,或者把问题上交到责任链中进行解决。
与呼叫中心的服务代表一样,普通对象也拥有独立动作所需的信息和方法。然而有时候,我们需要将这些责任集中,而无需理会对象常规的独立操作。在责任型模式中,有些设计模式就是用于满足这类需求;而有些责任型模式用于提升对象请求,以及将对象与依赖它的其他对象相分离。面向责任的模式提供用于集中、提升以及限制普通对象责任的若干技术。
图1 这张类图所示的类结构至少存在10处责任分配问题
这张类图有这样一些问题:
(1)Rocket.thrust()方法返回一个Rocket对象,而不是一个数字或者物理量。(实际上这里应该返回double型推力)
(2)LiquidRocket类有一个getLocation()方法,但是,无论是该图还是本例所涉及的问题领域,都没有说明火箭对象模型含有位置属性。即使是应该这样,我们也没有理由仅为液体燃料火箭的对象模型添加该属性,而不为其他的Rocket对象模型添加该属性。
(3)isLiquid()方法可以使用instanceof操作符,不过,那样我们必须在其超类中提供一个能够返回false的isLiquid()方法。
(4)CheapRockets采用了复数形式,而类名通常都是单数。
(5)CheapRockets类实现Runable,尽管这个接口与问题域的廉价火箭对象没有任何关系。
(6)可以利用类中的一个属性来对廉价这个特点建模,而无需为廉价火箭创建专门的类。
(7)引入CheapRockets类会导致我们无法区分一枚廉价的火箭到底是使用液体燃料还是固体燃料。例如,引入该类之后,我们无法对廉价的液体火箭建模。
(8)在这个模型中,FireWork类被声明为LiquidRocket类的一个子类,即表明所有的焰火都是液体燃料火箭推进的,这个观点是错误的。
(9)这个模型显示Reservation类与焰火类型之间存在直接的关联,而实际上本问题领域并不存在这样的关系。
(10)Reservation类拥有City对象的副本,而它可以从Location对象那里获取city对象。
(11)CheapRockets类由Runnable对象组成,这有点奇怪!
面对不同问题域时,我们应该在如下两方面做出权衡:建立一组策略来严格限制访问以降低自身责任;提供合适的扩展灵活性。出也就是要权衡代码可见性的安全性和灵活性。
超越普通责任型模式:不论类如何限制访问它的成员,面向对象的软件开发通常都将责任分配给各个对象。换句话说,面向对象开发提升了封装的作用,即一个对象只应该对自己的数据进行操作。
对责任进行分配是面向对象软件开发的规范做法。但是有几种设计模式并不是这样做的,它们将责任集中到某个中间对象或特殊对象。例如,Singleton模式将责任集中到单个对象上,并提供对这个对象的全局访问。记住Singleton模式和其他几个模式的意图的一个方法就是把它们作为普通的责任分配规则的反例。
如果你期望:将责任集中到某个类的单个实例中,可以应用Singleton模式
使得某个对象不必关心有哪些对象依赖自己,可以应用Observer模式
将责任集中到某个类中,这个类可以监视其他对象的交互,可以应用Mediator模式
使得一个对象代表另一个对象进行各种操作,可以应用Proxy模式
能够允许一个请求沿着对象链不断向上提交,直到这个请求被某一级处理,可以使用Chain of Responsibility模式。
将共享的细粒度对象的责任集中在一起,可以使用Flyweight模式。
每种设计模式只能解决特定情形下的问题。当我们不需要按照通常的准则将责任尽可能分配出去的时候,可以使用面向责任的设计模式。