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

简介: <p style="color:rgb(51,51,51); font-family:Arial; font-size:14px; line-height:26px"> <strong><span style="font-size:12px; color:rgb(51,51,255); font-family:'Microsoft YaHei'; line-height:30px"><a

 设计模式(十二)职责链模式(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。也是个不纯的职责链,因为每个对象可能处理一部分后,就需要传给下个对象来处理。

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

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

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

[php]  view plain  copy
 print ?
  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数组就可以了,而不用修改程序。

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

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

10.与其他相关模式

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

11.总结

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

      职责链模式的主要优点在于可以降低系统的耦合度,简化对象的相互连接,同时增强给对象指派职责的灵活性,增加新的请求处理类也很方便;其主要缺点在于不能保证请求一定被接收,且对于比较长的职责链,请求的处理可能涉及到多个处理对象,系统性能将受到一定影响,而且在进行代码调试时不太方便。
目录
相关文章
|
4月前
|
设计模式 网络协议 数据可视化
Java 设计模式之状态模式:让对象的行为随状态优雅变化
状态模式通过封装对象的状态,使行为随状态变化而改变。以订单为例,将待支付、已支付等状态独立成类,消除冗长条件判断,提升代码可维护性与扩展性,适用于状态多、转换复杂的场景。
450 0
|
9月前
|
设计模式 算法 前端开发
【设计模式】【行为型模式】职责链模式(Chain of Responsibility)
一、入门 什么是职责链模式? 职责链模式是一种行为设计模式,它允许你将请求沿着一条链传递,直到有对象处理它为止。每个对象都有机会处理请求,或者将其传递给链中的下一个对象。 为什么需要职责链模式? 使用
342 16
|
11月前
|
设计模式 存储 Java
【再谈设计模式】备忘录模式~对象状态的守护者
备忘录模式属于行为型设计模式。它的主要目的是在不破坏对象封装性的前提下,捕获并外部化一个对象的内部状态,以便之后可以将该对象恢复到这个状态。原发器(Originator):创建一个备忘录,用于记录当前时刻它的内部状态。原发器还可以使用备忘录来恢复其内部状态。备忘录(Memento):存储原发器对象的内部状态。备忘录应该防止原发器以外的其他对象访问其内部状态。负责人(Caretaker):负责保存备忘录,但不能对备忘录的内容进行操作或检查。
372 82
|
12月前
|
设计模式 供应链 安全
【再谈设计模式】中介者模式 - 协调对象间交互的枢纽
中介者模式定义了一个中介对象来封装一组对象之间的交互方式。中介者使得各对象之间不需要显式地相互引用,从而降低了它们之间的耦合度。它通过将对象之间的交互逻辑集中到中介者对象中,使得系统的结构更加清晰,易于维护和扩展。
297 18
【再谈设计模式】中介者模式 - 协调对象间交互的枢纽
|
设计模式 JavaScript 前端开发
js设计模式【详解】—— 职责链模式
js设计模式【详解】—— 职责链模式
201 8
|
设计模式 Java Go
【再谈设计模式】状态模式~对象行为的状态驱动者
状态模式属于行为型设计模式。它将对象的行为封装在不同的状态类中,使得对象在不同的状态下表现出不同的行为。上下文(Context):这是一个包含状态对象的类,它定义了客户感兴趣的接口,并维护一个具体状态对象的引用。上下文将操作委托给当前的状态对象来处理。抽象状态(State):这是一个抽象类或者接口,它定义了一个特定状态下的行为接口。所有具体的状态类都实现这个接口。具体状态(Concrete State):这些是实现抽象状态接口的类,每个具体状态类实现了与该状态相关的行为。
490 18
|
12月前
|
设计模式 算法 Java
【再谈设计模式】访问者模式~操作对象结构的新视角
  访问者模式是一种行为设计模式,旨在解决对象结构与操作逻辑的耦合问题。在软件系统开发中,当面临复杂的对象结构(如多种类型对象组成的树形或图形结构),且需要对这些对象执行不同操作时,传统方式将操作直接写在对象类中会导致类职责过多,不利于维护和扩展。而访问者模式通过将操作与对象结构分离,允许在不改变现有对象结构的情况下定义新操作,元素接受访问者访问,访问者定义对不同类型元素的操作逻辑,从而为应对这种复杂情况提供了有效的解决方案。
238 0
|
设计模式 存储 安全
18 Java反射reflect(类加载+获取类对象+通用操作+设计模式+枚举+注解)
18 Java反射reflect(类加载+获取类对象+通用操作+设计模式+枚举+注解)
412 0
|
9月前
|
设计模式 Java 数据库连接
【设计模式】【创建型模式】工厂方法模式(Factory Methods)
一、入门 什么是工厂方法模式? 工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个用于创建对象的接口,但由子类决定实例化哪个类。工厂方法模式使类的实例化延迟
277 16
|
9月前
|
设计模式 负载均衡 监控
并发设计模式实战系列(2):领导者/追随者模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第二章领导者/追随者(Leader/Followers)模式,废话不多说直接开始~
265 0