设计模式(十二)职责链模式(Chain of Responsibility)(对象行为型)

1.概述

你去政府部门求人办事过吗?有时候你会遇到过官员踢球推责,你的问题在我这里能解决就解决,不能解决就推卸给另外个一个部门(对象)。至于到底谁来解决这个问题呢?政府部门就是为了可以避免屁民的请求与官员之间耦合在一起,让多个(部门)对象都有可能接收请求,将这些(部门)对象连接成一条链,并且沿着这条链传递请求,直到有(部门)对象处理它为止。

例子1:js的事件浮升机制

例子2:

2.问题

如果有多个对象都有可能接受请求,如何避免避免请求发送者与接收者耦合在一起呢?

3.解决方案

职责链模式(Chain of Responsibility)使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。(Avoid coupling the sender of a request to itsreceiver by giving morethan one objecta chance to handle the request.Chain the receiving objects andpassthe request along the chain until an object handles it. )

1)在职责链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。
2)请求在这条链上传递,直到链上的某一个对象处理此请求为止。
3)发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织链和分配责任。

4.适用性

在以下条件下使用Responsibility 链:

• 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。

• 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。

•可动态指定一组对象处理请求。

5.结构

一个典型的对象结构可能如下图所示:

6. 模式的组成

抽象处理者角色(Handler:Approver):定义一个处理请求的接口,和一个后继连接(可选)

具体处理者角色(ConcreteHandler:President):处理它所负责的请求,可以访问后继者,如果可以处理请求则处理,否则将该请求转给他的后继者。

客户类(Client):向一个链上的具体处理者ConcreteHandler对象提交请求。

7. 效果

Responsibility 链有下列优点和缺点( l i a b i l i t i e s ) :

职责链模式的优点:

1 ) 降低耦合度 :该模式使得一个对象无需知道是其他哪一个对象处理其请求。对象仅需知道该请求会被“正确”地处理。接收者和发送者都没有对方的明确的信息,且链中的对象不需知道链的结构。

2) 职责链可简化对象的相互连接 :    结果是,职责链可简化对象的相互连接。它们仅需保持一个指向其后继者的引用,而不需保持它所有的候选接受者的引用。

3) 增强了给对象指派职责( R e s p o n s i b i l i t y )的灵活性 :当在对象中分派职责时,职责链给你更多的灵活性。你可以通过在运行时刻对该链进行动态的增加或修改来增加或改变处理一个请求的那些职责。你可以将这种机制与静态的特例化处理对象的继承机制结合起来使用。

4)增加新的请求处理类很方便

职责链模式的缺点:
1) • 不能保证请求一定被接收。既然一个请求没有明确的接收者,那么就不能保证它一定会被处理 —该请求可能一直到链的末端都得不到处理。一个请求也可能因该链没有被正确配置而得不到处理。
2) • 系统性能将受到一定影响,而且在进行代码调试时不太方便;可能会造成循环调用。
8. 纯与不纯的职责链模式
 
纯的职责链模式:一个具体处理者角色处理只能对请求作出两种行为中的一个:一个是自己处理(承担责任),另一个是把责任推给下家。不允许出现某一个具体处理者对象在承担了一部分责任后又将责任向下传的情况。请求在责任链中必须被处理,不能出现无果而终的结局。
反之就是不纯的职责链模式。  
在一个纯的职责链模式里面,一个请求必须被某一个处理者对象所接收;在一个不纯的职责链模式里面,一个请求可以最终不被任何接收端对象所接收。
 

9.实现

我们先来看不纯的职责模式:

假如在公司里,

如果你的请假时间小于0.5天,那么只需要向leader打声招呼就OK了。
如果0.5<请假天数<=3天,需要先leader打声招呼,要不然leader不知你跑哪里,然后部门经理直接签字。
如果3<请假天数 天,需要先leader打声招呼,然后到部门经理签字,最好总经经理确认签字,

当你看到这情况后你心里是不是已经有了自己的想法了?写一系列的if语句来一条条的判断.但这样的写法虽然可以实现目前的需求,可如果当流程改了呢?我请假超过3天,告诉leader和总经理签字就可以,那你又得一步一步修改程序。如果if语句的条数发生变化的话我们还必须在代码中添加必要的if判断,这对于程序的维护来说是相当麻烦的.如果我们使用职责链模式的话就可以相当简单了.

这个例子就是个list。也是个不纯的职责链,因为每个对象可能处理一部分后,就需要传给下个对象来处理。

  1. <?php
  2. /**
  3. * 加入在公司里,如果你的请假时间小于0.5天,那么只需要向leader打声招呼就OK了。
  4.   如果0.5<请假天数<=3天,需要先leader打声招呼,要不然leader不知你跑哪里,然后部门经理直接签字。
  5.   如果3<请假天数 天,需要先leader打声招呼,然后到部门经理签字,最好总经经理确认签字,
  6.   这样就是个list。也是个不纯的职责链,因为每个对象可能处理一部分后,就需要传给下个对象来处理。
  7.   
  8. */
  9. /**
  10. * 纯职责链模式
  11. *
  12. * 为解除请求的发送者和接收者之间的耦合,而使用多个对象都用机会处理这个请求,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它
  13. * @author guisu
  14. *
  15. */
  16. /**
  17. * 抽象处理者角色(Handler:Approver):定义一个处理请求的接口,和一个后继连接(可选)
  18. *
  19. */
  20. abstract class Handler
  21. {
  22. protected $_handler = null;
  23. protected $_handlerName = null;
  24. public function setSuccessor($handler)
  25. {
  26. $this->_handler = $handler;
  27. }
  28. protected  function _success($request)
  29. {
  30. echo $request->getName(), '\' request was passed  <br/>';
  31. return true;
  32. }
  33. abstract function handleRequest($request);
  34. }
  35. /**
  36. * 具体处理者角色(ConcreteHandler:President):处理它所负责的请求,可以访问后继者,如果可以处理请求则处理,否则将该请求转给他的后继者。
  37. *
  38. */
  39. class ConcreteHandlerLeader extends Handler
  40. {
  41. function __construct($handlerName){
  42. $this->_handlerName = $handlerName;
  43. }
  44. public function handleRequest($request)
  45. {
  46. echo $this->_handlerName, ' was known <br/>';//已经跟leader招呼了
  47. if($request->getDay() < 0.5) {
  48. return $this->_success($request);
  49. }
  50. if ($this->_handler instanceof Handler) {
  51. return $this->_handler->handleRequest($request);
  52. }
  53. }
  54. }
  55. /**
  56. * Manager
  57. *
  58. */
  59. class ConcreteHandlerManager extends Handler
  60. {
  61. function __construct($handlerName){
  62. $this->_handlerName = $handlerName;
  63. }
  64. public function handleRequest($request)
  65. {
  66. echo $this->_handlerName, " was signed <br/>";//部门经理签字
  67. if( $request->getDay() > 0.5 && $request->getDay()<=3) {
  68. return $this->_success($request);
  69. }
  70. if ($this->_handler instanceof Handler) {
  71. return $this->_handler->handleRequest($request);
  72. }
  73. }
  74. }
  75. class ConcreteHandlerGeneralManager extends Handler
  76. {
  77. function __construct($handlerName){
  78. $this->_handlerName = $handlerName;
  79. }
  80. public function handleRequest($request)
  81. {
  82. echo $this->_handlerName, " was signed <br/>";//总经理签字
  83. if(3 < $request->getDay()){
  84. return $this->_success($request);
  85. }
  86. if ($this->_handler instanceof Handler) {
  87. return $this->_handler->handleRequest($request);
  88. }
  89. }
  90. }
  91. /**
  92. * 请假申请
  93. *
  94. */
  95. class   Request
  96. {
  97. private $_name;
  98. private $_day;
  99. private $_reason;
  100. function __construct($name= '', $day= 0, $reason = ''){
  101. $this->_name = $name;
  102. $this->_day = $day;
  103. $this->_reason = $reason;
  104. }
  105. public function setName($name){
  106. $this->_name = $name;
  107. }
  108. public function getName(){
  109. return  $this->_name;
  110. }
  111. public function setDay($day){
  112. $this->_day = $day;
  113. }
  114. public function getDay(){
  115. return  $this->_day ;
  116. }
  117. public function setReason($reason ){
  118. $this->_reason = $reason;
  119. }
  120. public function getReason( ){
  121. return  $this->_reason;
  122. }
  123. }
  124. class client{
  125. /**
  126. *流程1:leader-> manager ->generalManager
  127. *
  128. */
  129. static function main(){
  130. $leader = new ConcreteHandlerLeader('$leader');
  131. $manager = new ConcreteHandlerManager('$manager');
  132. $generalManager = new ConcreteHandlerGeneralManager('$generalManager');
  133. //请求实例
  134. $request = new Request('guisu',4,'休息' );
  135. $leader->setSuccessor($manager);
  136. $manager->setSuccessor($generalManager);
  137. $result =  $leader->handleRequest($request);
  138. }
  139. /**
  140. * 流程2 :
  141. * leader ->generalManager
  142. */
  143. static function main2(){
  144. //签字列表
  145. $leader = new ConcreteHandlerLeader('$leader');
  146. $manager = new ConcreteHandlerManager('$manager');
  147. $generalManager = new ConcreteHandlerGeneralManager('$generalManager');
  148. //请求实例
  149. $request = new Request('guisu',3,'休息' );
  150. $leader->setSuccessor($generalManager);
  151. $result = $leader->handleRequest($request);
  152. }
  153. /**
  154. * 流程3 :如果leader不在,那么完全可以写这样的代码
  155. * manager ->generalManager
  156. */
  157. static function main3(){
  158. //签字列表
  159. $leader = new ConcreteHandlerLeader('$leader');
  160. $manager = new ConcreteHandlerManager('$manager');
  161. $generalManager = new ConcreteHandlerGeneralManager('$generalManager');
  162. //请求实例
  163. $request = new Request('guisu',0.1,'休息' );
  164. $leader->setSuccessor($manager);
  165. $manager->setSuccessor($generalManager);
  166. $result = $manager->handleRequest($request);
  167. }
  168. }
  169. client::main3();

对于怎么维护职责的链子,《设计模式》仅仅说自己去实现,可以使用list或者map的形式。

我们吧把职责链模式应用到面向过程编程,而不是对象。例如:

  1. 个税起征点3500元
  2. 级数   全月应纳税所得额            税率(%)
  3. 1    不超过1500元的                3
  4. 2    超过1500元至4500元的部分      10
  5. 3    超过4500元至9000元的部分      20
  6. 4    超过9000元至35000元的部分     25
  7. 5    超过35000元至55000元的部分    30
  8. 6    超过55000元至80000元的部分    35
  9. 7    超过80000元的部分              45

我们可以不必使用那么多的if和elseif语句判断。我们只要配置$taxs数组就可以了,而不用修改程序。

  1. <?php
  2. /**
  3. * 个税起征点3500元
  4. 级数   全月应纳税所得额            税率(%)
  5.    1    不超过1500元的                3
  6.    2    超过1500元至4500元的部分      10
  7.    3    超过4500元至9000元的部分      20
  8.    4    超过9000元至35000元的部分     25
  9.    5    超过35000元至55000元的部分    30
  10.    6    超过55000元至80000元的部分    35
  11.    7    超过80000元的部分             45
  12. */
  13. /**
  14. * 这个例子还没有扣除社保公积金等
  15. */
  16. //收入
  17. $income = 84000;
  18. //税率
  19. $taxs[1] = array(1500, 0.03);
  20. $taxs[2] = array(4500, 0.1);
  21. $taxs[3] = array(9000, 0.2);
  22. $taxs[4] = array(35000, 0.25);
  23. $taxs[5] = array(55000, 0.30);
  24. $taxs[6] = array(80000, 0.35);
  25. $taxs[7] = array(1000000000, 0.45);
  26. /**
  27. * 计算税率
  28. *
  29. * @param int $income
  30. * @return int
  31. */
  32. function compTax($income){
  33. global $taxs;
  34. //个税起点
  35. $taxStart  = 3500;
  36. $incomeTax = $income > $taxStart ?($income - $taxStart) : 0;
  37. $flag = false;
  38. foreach ($taxs as $values) {
  39. if ($incomeTax < $values[0]  ) {
  40. $compTax = $incomeTax * $values[1];
  41. break;
  42. }else{
  43. continue;
  44. }
  45. }
  46. return $compTax;
  47. }
  48. echo compTax($income);
  49. echo '-------------------<br/>';

如果判断的条件很多,也就是数组$taxs很庞大。那么我们可以使用折半查找的方式:

  1. <?php
  2. /**
  3. * 个税起征点3500元
  4. 级数   全月应纳税所得额            税率(%)
  5.    1    不超过1500元的                3
  6.    2    超过1500元至4500元的部分      10
  7.    3    超过4500元至9000元的部分      20
  8.    4    超过9000元至35000元的部分     25
  9.    5    超过35000元至55000元的部分    30
  10.    6    超过55000元至80000元的部分    35
  11.    7    超过80000元的部分             45
  12. */
  13. /**
  14. * 这个例子还没有扣除社保公积金等
  15. */
  16. //收入
  17. $income = 84000;
  18. //税率
  19. $taxs[1] = array(1500, 0.03);
  20. $taxs[2] = array(4500, 0.1);
  21. $taxs[3] = array(9000, 0.2);
  22. $taxs[4] = array(35000, 0.25);
  23. $taxs[5] = array(55000, 0.30);
  24. $taxs[6] = array(80000, 0.35);
  25. $taxs[7] = array(1000000000, 0.45);
  26. /**
  27. * 优化计算税率:使用折半查找法,有效缩短时间复杂度
  28. */
  29. /**
  30. * 优化计算税率:折半查找法
  31. *
  32. * @param int $income
  33. * @return int
  34. */
  35. function optimizeCompTax($income){
  36. //个税起点
  37. global $taxs;
  38. $taxStart  = 3500;
  39. $incomeTax = $income > $taxStart ?($income - $taxStart) : 0;
  40. $key = bSearch($taxs, $incomeTax, 1);
  41. return $incomeTax * $taxs[$key][1];
  42. }
  43. /**
  44. *
  45. * 折半查找法
  46. * @param unknown_type $taxs
  47. * @param unknown_type $incomeTax
  48. * @return unknown
  49. */
  50. function bSearch($taxs, $incomeTax, $start = 0){
  51. $incomeTax = intval($incomeTax);
  52. ksort($taxs);
  53. foreach ($taxs as $key => $values) {
  54. $low = $key;
  55. break;
  56. }
  57. if ($incomeTax <=0 ) {
  58. return $low;
  59. }
  60. $high = count($taxs) + $low -1;
  61. while  ( $low < $high){
  62. $mid = intval(($low + $high)/2) ;
  63. if ( $incomeTax < $taxs[$mid][0] ) {//后半区找
  64. $high = $mid;
  65. } else { //前半区找
  66. $low = $mid ;
  67. }
  68. /**
  69. * 由于这个不是完全折半查找
  70. * 只有两个元素的时候,需要判断
  71. */
  72. if (($high - $low) ==1) {
  73. if ( $incomeTax > $taxs[$low][0] ) {
  74. $key = $high;
  75. } else{
  76. $key = $low;
  77. }
  78. break;
  79. }
  80. }
  81. return $key;
  82. }
  83. echo optimizeCompTax($income);

10.与其他相关模式

职责链常与Composite组合模式一起使用。这种情况下,一个构件的父构件可作为它的后继

11.总结

      

职责链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织链和分配责任。

      职责链模式的主要优点在于可以降低系统的耦合度,简化对象的相互连接,同时增强给对象指派职责的灵活性,增加新的请求处理类也很方便;其主要缺点在于不能保证请求一定被接收,且对于比较长的职责链,请求的处理可能涉及到多个处理对象,系统性能将受到一定影响,而且在进行代码调试时不太方便。

设计模式(十二)职责链模式(Chain of Responsibility)(对象行为型)的更多相关文章

  1. 设计模式 ( 十二 ) 职责链模式(Chain of Responsibility)(对象行为)

     设计模式(十二)职责链模式(Chain of Responsibility)(对象行为型) 1.概述 你去政府部门求人办事过吗?有时候你会遇到过官员踢球推责,你的问题在我这里能解决就解决.不能解决就 ...

  2. 责任链模式 职责链模式 Chain of Responsibility Pattern 行为型 设计模式(十七)

    责任链模式(Chain of Responsibility Pattern) 职责链模式 意图 使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系 将这些对象连接成一条链,并沿着这 ...

  3. 设计模式入门之职责链模式Chain Of Responsibility

    //职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. //实例:申请费用的功能,不同金额的费 ...

  4. atitit.设计模式(1)--—职责链模式(chain of responsibility)最佳实践O7 日期转换

    atitit.设计模式(1)---职责链模式(chain of responsibility)最佳实践O7 日期转换 1. 需求:::日期转换 1 2. 可以选择的模式: 表格模式,责任链模式 1 3 ...

  5. 职责链模式(Chain of Responsibility)(对象行为型)

    1.概述 你去政府部门求人办事过吗?有时候你会遇到过官员踢球推责,你的问题在我这里能解决就解决,不能解决就推卸给另外个一个部门(对象).至于到底谁来解决这个问题呢?政府部门就是为了可以避免屁民的请求与 ...

  6. 重温设计模式(三)——职责链模式(chain of responsibility)

    一. 写在前面的 这么多的设计模式,我觉得职责链是我第一次看上去最简单,可是回想起来却又最复杂的一个模式. 因此,这个文章我酝酿了很久,一直也没有胆量发出来,例子也是改了又改,可是仍然觉得不够合理.所 ...

  7. 职责链模式(chain of responsibility)

    一. 写在前面的 这么多的设计模式,我觉得职责链是我第一次看上去最简单,可是回想起来却又最复杂的一个模式. 因此,这个文章我酝酿了很久,一直也没有胆量发出来,例子也是改了又改,可是仍然觉得不够合理.所 ...

  8. 设计模式之职责链模式(Chain of Responsibility)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于怎样创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...

  9. 设计模式:职责链模式(Chain of Responsibility)

    去年参加校招要到长沙来,这个对于我来说不是特别喜欢(但又必须的来,谁叫咱不是985.211的娃呢),但是对于某些人来说就是福音了.大四还有课,而且学校抓的比较严,所以对于那些想翘课的人来说这个是最好不 ...

随机推荐

  1. 帝国cms语句调用

    帝国cms系统,灵动标签,有着非常强大的数据调用功能.这里为广大菜鸟站长普及一下. 我们来看这段代码. [e:loop={,,}] <li>·<a target="_bla ...

  2. Google日历添加农历、节日和天气插件(步骤)

    Google日历添加农历.节日和天气插件(步骤) Google功能非常多,Google日历只是其中一个,而且支持Exchange账户(iPhone,WP7,诺基亚等)和Google账户登录(andro ...

  3. 在C#中调用API获取网络信息和流量

    原文 在C#中调用API获取网络信息和流量 最近一项目中要求显示网络流量,而且必须使用C#. 事实上,调用 IpHlpApi.dll 的 GetIfTable API 可以轻易获得网络信息和网络流量. ...

  4. QT在ui文件上建立信号操机制会不会对后期维护产生影响 - love4Mario的专栏 - 博客频道 - CSDN.NETQT在ui文件上建立信号操机制会不会对后期维护产生影响 - love4Mario的专栏 - 博客频道 - CSDN.NET

    QT在ui文件上建立信号操机制会不会对后期维护产生影响 - love4Mario的专栏 - 博客频道 - CSDN.NET QT在ui文件上建立信号操机制会不会对后期维护产生影响 分类: 学习心得 2 ...

  5. 带你轻松玩转Git--图解三区结构

    在上篇文章的结尾我们提到了Git 的三区结构,在版本控制体系中有这样两种体系结构,一种是两区结构一种是三区结构.接下来我们通过对Git三区的结构学习来帮助我们更好的去理解并运用Git. 两区结构是其他 ...

  6. Foundation Sorting: Quicksort

    /* Quick Sorting. * Implementation history:. * 2013-09-15, Mars Fu, first version. */ /* [Quicksort ...

  7. 解决shell脚本中 echo 怎么写入换行到文件

    測试环境:ubuntu12.04 LTS版本号 echo >> file.txt就可以, 这样的方法对于脚本开头是bash和sh效果都一样, 而echo -e >> file. ...

  8. c 判断水仙花数,质数(素数)

    #include<stdio.h> #include<stdbool.h> //水仙花数--各位立方和等于本身 void sXh() { int x,y,z; printf(& ...

  9. linux命令: patch

    一. 针对单文件的patch: 我们以mkprj.sh.1和mkprj.sh两个文件为例: [root@localhost tst]# lsmkprj.sh.1  mkprj.sh 看两个文件的差异: ...

  10. spring bean管理 笔记1

    轻量级,无侵入 Bean管理 1 创建applicationContext.xml 2 配置被管理的Bean 3 获取Bean pom.xml配置 <dependency> <gro ...