设计模式 ( 十二 ) 职责链模式(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月前
|
设计模式 JavaScript 前端开发
js设计模式【详解】—— 职责链模式
js设计模式【详解】—— 职责链模式
77 8
|
5月前
|
设计模式
**工厂模式与抽象工厂模式**都是创建型设计模式,用于封装对象创建,减少耦合
【6月更文挑战第23天】**工厂模式与抽象工厂模式**都是创建型设计模式,用于封装对象创建,减少耦合。工厂模式专注于单个对象,通过具体工厂创建具体产品,适用于简单对象创建;抽象工厂则关注一系列相关产品,提供创建一族对象的接口,适用于处理多个不兼容产品族。选择模式基于问题域的复杂性,单个产品需求时用工厂模式,多产品族时用抽象工厂模式。
33 5
|
3月前
|
设计模式 存储 安全
18 Java反射reflect(类加载+获取类对象+通用操作+设计模式+枚举+注解)
18 Java反射reflect(类加载+获取类对象+通用操作+设计模式+枚举+注解)
96 0
|
5月前
|
设计模式 缓存 Java
Java设计模式:享元模式实现高效对象共享与内存优化(十一)
Java设计模式:享元模式实现高效对象共享与内存优化(十一)
|
5月前
|
设计模式
职责链模式-大话设计模式
职责链模式-大话设计模式
|
6月前
|
设计模式 安全 Java
设计模式之责任链 Chain Of Responsibility
设计模式之责任链 Chain Of Responsibility
40 1
|
5月前
|
设计模式
行为设计模式之职责链模式
行为设计模式之职责链模式
|
6月前
|
设计模式 Java
【设计模式】JAVA Design Patterns——Chain of responsibility(责任链模式)
【设计模式】JAVA Design Patterns——Chain of responsibility(责任链模式)
|
6月前
|
设计模式 Java API
【设计模式】JAVA Design Patterns——Active Object(活动对象设计模式)
【设计模式】JAVA Design Patterns——Active Object(活动对象设计模式)
|
6月前
|
设计模式 存储 SQL
第四篇 行为型设计模式 - 灵活定义对象间交互
第四篇 行为型设计模式 - 灵活定义对象间交互
129 0

热门文章

最新文章

  • 1
    C++一分钟之-设计模式:工厂模式与抽象工厂
    43
  • 2
    《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
    50
  • 3
    C++一分钟之-C++中的设计模式:单例模式
    58
  • 4
    《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
    38
  • 5
    《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
    64
  • 6
    Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
    59
  • 7
    Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
    42
  • 8
    Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
    50
  • 9
    Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
    112
  • 10
    Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
    78