问答题 阅读以下关于设计模式应用的叙述,根据要求回答问题。
[说明]
PH软件公司承接了某企业二期信息化软件开发项目,工程项目的研发任务之一是建设采购分级审批系统。该企业采购审批是根据采购金额的不同由不同层次的主管人员来审批,主任可以审批8万元以下(不包含8万元)的采购单,副董事长可以审批8~15万元(不包含15万元)的采购单,董事长可以审批15-45万元(不包含45万元)的采购单,45万元及以上的采购单就需要企业高层开会讨论决定。PH公司架构师采用某种设计模式设计的类图如图4—9所示。
问答题 [问题1]
请用350字以内的文字指出该公司架构师所采用的设计模式的具体名称、设计意图及其优缺点。
【正确答案】Chain of Responsibility(职责链)模式可以在系统中建立一个链,这样消息可以在首先接收到它的级别处被处理,或者可以定位到可以处理它的对象。依题意,该企业的采购审批是分级进行的,可以采用职责链(Chain of Responsibility)设计模式对该采购审批过程进行设计,设计后得到的类图如图4—9所示。
Chain of Responsibility(职责链)模式的设计意图是,使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系;将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。换而言之,其目的是为了将一个请求发送给一个对象集合,对象被组织成一条链,而负责处理该请求的对象将获取请求消息并加以处理,其余对象则仅仅负责将该请求消息按照责任链的顺序传递到下一个对象。因此责任链模式的关键在于组织不同的对象成为一条链并传递消息。根据题干给出的不同层次主管人员的审批额度“主任:5万元以下,副董事长:5万元~10万元,董事长:10万元~50万元,开会讨论:50万元及以上”,对象在职责链中的顺序应该为:Director→Vicepresident→Presiden→CongressMeeting。由于主任的审批额度最小,因此审批的请求应该从主任(Director)开始。
Chain of Responsibility模式有以下一些优点和缺点。
(1)降低了耦合度。该模式使得一个对象无须知道是其他哪一个对象处理其请求。对象仅需知道该请求会被“正确”地处理。接收者和发送者都没有对方的明确的信息,且链中的对象不需要知道链的结构。结果是,职责链可简化对象的相互连接。它们仅需要保持一个指向其后继者的引用,而不需要保持它所有的候选接受者的引用。
(2)增加了给对象指定责任(Responsibility)的灵活性。当在对象中分派职责时,职责链给出更多的灵活性。可以通过在运行时刻对该链进行动态的增加或修改来增加或改变处理一个请求的那些职责。可以将这种机制与静态的特例化处理对象的继承机制结合起来使用。
(3)由于在一个类中产生的事件可以被发送到组成中的其他类处理器上,因此类的集合可以作为一个整体。
(4)不保证被接受。既然一个请求没有明确的接收者,那么就不能保证它一定会被处理——该请求可能一直到链的末端都得不到处理。一个请求也可能因为该链没有被正确配置而得不到处理。
【答案解析】
问答题 [问题2]
请用300字以内的文字指出该公司架构师所采用的设计模式的适用性,以及图6—5中需要考虑哪些实现问题?
【正确答案】在以下情况中,应该使用Chain of Responsibility模式。
(1)多个对象可以处理一个请求,而其处理器却是未知的。
(2)想要在不指定确切的请求接收对象的情况下,向几个对象中的一个发送请求。
(3)可以动态地指定能够处理请求的对象集等。
依题意,在图4—9中,需要考虑以下一些职责链模式实现问题。
(1)实现后继者。链有两种方法可以实现后继者链:①定义新的链接(通常在类Approver中定义,但也可由类Director、类Vicepresident、类Presiden和类Congress来定义);②使用已有的链接。
通常,可使用已有的对象引用来形成后继者链。例如,在一个部分一整体层次结构中,父构件引用可定义一个部件的后继者,窗口组件(Widget)结构可能早已有这样的链接。在Composite模式中更详细地讨论了父构件引用。当已有的链接能够支持所需的链时,完全可以使用它们。这样就不需要明确定义链接,而且可以节省空间。但如果该结构不能反映应用所需的职责链,那么就必须定义额外的链接。
(2)连接后继者。如果没有已有的引用可定义一个链,那么就必须自己引入它们。这种情况下类Approver不仅定义该请求的接口,通常也维护后继链接。这样类Approver就提供了ApproverRequest的缺省实现,ApproverRequest向后继者(如果有的话)转发请求。如果类Director的子类对该请求不感兴趣,它不需要重定义转发操作,因为它的缺省实现进行无条件的转发。
(3)表示请求。可以用不同的方法表示请求。最简单的形式,请求是一个硬编码的(hard-coded)操作调用。这种形式方便而且安全,但只能转发Approver类定义的固定的一组请求。
另一选择是使用一个处理函数,这个函数以一个请求码(如一个整型常数或一个字符串)为参数。这种方法支持请求数目不限。唯一的要求是发送方和接受方在请求如何编码问题上应达成一致。该方法更为灵活,但它需要用条件语句来区分请求代码以分派请求。另外,无法用类型安全的方法来传递请求参数,因此它们必须被手工打包和解包。显然,相对于直接调用一个操作而言它不太安全。
为解决参数传递问题,可以使用独立的请求对象来封装请求参数。Request类可明确地描述请求,而新类型的请求可用它的子类来定义。这些子类可定义不同的请求参数。处理者必须知道请求的类型(即它们正使用哪一个Request子类)以访问这些参数。为标识请求,Request可定义一个访问器(accessor)函数以返回该类的标识符。或者,如果实现语言支持的话,接受者可使用运行时的类型信息。
(4)在Smalltalk中自动转发。你可以使用Smalltalk中的doseNotUnderstand机制转发请求。没有相应方法的消息被doseNotUnderstand的实现捕捉(trap in),此实现可被重定义,从而可向一个对象的后继者转发该消息。这样就不需要手工实现转发;类仅处理它感兴趣的请求,而依赖doesNotUnderstand转发所有其他的请求。
【答案解析】
问答题 [问题3]
结合你的系统架构经验,请用300字以内的文字指出Command模式、Observer模式、Chain ofResponsibility模式和Mediator模式在发送者和接收者解耦方面的区别。
【正确答案】当合作的对象直接互相引用时,它们变得互相依赖,这可能会对一个系统的分层和重用性产生负面影响。Command模式、Obserwer模式、Chain of Responsibility模式和Mediator模式都涉及如何对发送者和接收者解耦,但它们又各有不同的权衡考虑。
命令模式使用一个Command对象来定义一个发送者和一个接收者之间的绑定关系,从而支持解耦,如图4—16所示。Command对象提供了一个提交请求的简单接口(即Execute操作)。将发送者和接收者之间的连接定义在一个单独的对象使得该发送者可以与不同的接收者一起工作。这就将发送者与接收者解耦,使发送者更易于复用。此外,可以复用Command对象,用不同的发送者参数化一个接收者。虽然Command模式描述了避免使用生成子类的实现技术,名义上每一个发送者一接收者连接都需要一个子类。
观察者模式通过定义一个接口来通知目标中发生的改变,从而将发送者(目标)与接收者(观察者)解耦,如图4-17所示。Observer定义了一个比Command更松的发送者一接收者绑定,因为一个目标可能有多个观察者,并且其数目可以在运行时变化。观察者模式中的Subject和Observer接口是为了处理Subject的变化而设计的,因此当对象间有数据依赖时,最好用观察者模式来对它们进行解耦。


职责链模式通过沿一个潜在接收者链传递请求而将发送者与接收者解耦,如图4-18所示。因为发送者和接收者之间的接口是固定的,职责链可能也需要一个定制的分发策略。因此它与Mediator一样存在类型安全的问题。如果职责链已经是系统结构的一部分,同时在链上的多个对象中总有一个可以处理请求,那么职责链将是一个很好地将发送者和接收者解耦的方法。此外,因为链可以被简单的改变和扩展,从而该模式提供了更大的灵活性。
中介者模式让对象通过一个Mediator对象间接的互相引用,从而对它们解耦,如图4—19所示。一个Mediator对象为各Colleague对象间的请求提供路由并集中它们的通信。因此各Colleague对象仅能通过Mediator接口相互交谈。因为这个接口是固定的,为增加灵活性Mediator可能不得不实现它自己的分发策略。可以用一定方式对请求编码并打包参数,使得Colleague对象可以请求的操作数目不限。中介者模式可以减少一个系统中的子类生成,因为它将通信行为集中到一个类中而不是将其分布在各个子类中。然而,特别的分发策略通常会降低类型安全性。

【答案解析】