设计模式(十二)职责链模式(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. 帝国cms7.2自定义列表建立tag效果 代码 教程

    统计记录:(如:select count(*) as total from phome_ecms_news where classid=1 and checked=1) 注:这句SQL的意思是查找统计 ...

  2. Linux第三方源

    由于版权等各方面原因,很多时候在基础安装完Linux后,满多软件并不包含在yum(对于Ubuntu可能是apt-get)源中. 因此可以去下载第三方源,安装下载第三方软件.当然,如果习惯了源代码编译安 ...

  3. ie6与固定定位fixed,+ 条件注释格式注意

    ie6并不支持position:fixed, ie7+都支持fixed定位, ie6固定定位实现方法1: <!DOCTYPE html> <html> <head> ...

  4. 什么是PCB改板及PCB改板应注意的问题

    PCB改板是指在保持原有功能一致的前提下,对原有产品设计及电路板布局走线设计的基础上进行整改设计,调整板上器件布局与线路走向,实现电子产品重新设计研发,同时又可以规避知识产权等纠纷,加快新产品研发速度 ...

  5. <Win32_16>来看看标准菜单和右键菜单的玩法

    日常应用中,菜单主要分为两种:(1) 标准菜单(处于应用程序菜单栏处的菜单)    (2)右键快捷菜单 几乎你所见过或使用过的软件中,都有它俩儿 为应用程序添加它们的基本步骤: (1)用代码或者IDE ...

  6. Book of Evil 树双向DFS

    Book of Evil Paladin Manao caught the trail of the ancient Book of Evil in a swampy area. This area ...

  7. leetcode_question_67 Add Binary

    Given two binary strings, return their sum (also a binary string). For example, a = "11" b ...

  8. Ext JS学习第五天 Ext_window组件(一)

    此文来记录学习笔记 •第一个组件:Ext.window.Window.对于组件,也就是Ext最吸引开发者的地方,那么我们要真正的使用Ext的组件,首先必须学会阅读API文档. –xtype:组件的别名 ...

  9. 纯CSS写九宫格样式,高宽自适应正方形

    <!DOCTYPE html><html>    <head>        <meta charset="utf-8">      ...

  10. strcpy_s

    char src[5]="abcd"; char *des=new char[str.length(src)+1];   // length()不计\0 strcpy_s(des, ...