设计模式(十二)职责链模式(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. * 加入在公司里,如果你的请假时间小于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的形式。

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

[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. * 个税起征点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很庞大。那么我们可以使用折半查找的方式:

[php] view plain copy

print?

  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.总结

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

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

时间: 2024-10-01 02:56:20

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

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

 设计模式(十二)职责链模式(Chain of Responsibility)(对象行为型) 1.概述 你去政府部门求人办事过吗?有时候你会遇到过官员踢球推责,你的问题在我这里能解决就解决.不能解决就推卸给另外个一个部门(对象).至于究竟谁来解决问题呢?政府部门就是为了能够避免屁民的请求与官员之间耦合在一起,让多个(部门)对象都有可能接收请求,将这些(部门)对象连接成一条链,而且沿着这条链传递请求.直到有(部门)对象处理它为止. 样例1:js的事件浮升机制 样例2: 2.问题 假设有多个对象都有

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

责任链模式(Chain of Responsibility Pattern) 职责链模式 意图 使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系 将这些对象连接成一条链,并沿着这条链传递请求,直到有一个对象处理它为止. 责任链模式中,每个对象通过持有对下家的引用而链接起来,形成一条链条,串联起来多个处理对象. 在责任链模式中,请求在链上进行传递,直到链上的某一个对象决定处理此请求. 发出这个请求的客户端程序并不知道到底是哪一个对象具体的处理了请求 这使得系统可以在不影响客户

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

//职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. //实例:申请费用的功能,不同金额的费用须要由不同的上级部分审核通过,假设下级没有审核的权限那么就让上级来进行审核 //上代码 //定义职责对象的接口 public abstract class Handler { protected Handler successor = null; public Handler(Handler su

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

atitit.设计模式(1)---职责链模式(chain of responsibility)最佳实践O7 日期转换 1. 需求:::日期转换 1 2. 可以选择的模式: 表格模式,责任链模式 1 3. 调用代码 2 4. 责任链链的特性: 2 5. 模式结构 4 6. 职责链模式包含如下角色:Handler,ConcreteHandler: 具体处理者,HandlerChainUtil ,Client 4 7. 设置哈一个handler,,两个法:排序法,指定法 5 1. 指定法 5 2. 排

设计模式(行为型)之职责链模式(Chain of Responsibility Pattern)

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 阅读前一篇<设计模式(行为型)之状态模式(State Pattern)>http://blog.csdn.net/yanbober/article/details/45502665 概述 职责链可以是一条直线.一个环或者一个树形结构,最常见的职责链是直线型,即沿着一条单向的链来传递请求.链

职责链模式(Chain of Responsibility)

详解 定义 为了避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止,职责链模式又称为责任链模式,它是一种对象行为型模式.(如果你接触过异常处理,那么套用异常处理机制可以更好地理解). 职责链可以是一条直线,也可以是一个环,还可以是一个树形结构,不过最常见的职责链是直线型,即沿着一条单向的链来传递请求.链上的每一个对象都是请求处理者,职责链模式可以将请求的处理者组织成一条链,并使请求沿着链传递,由链上的处理者对请求进行

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

职责链模式(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 obje

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

一. 写在前面的 这么多的设计模式,我觉得职责链是我第一次看上去最简单,可是回想起来却又最复杂的一个模式. 因此,这个文章我酝酿了很久,一直也没有胆量发出来,例子也是改了又改,可是仍然觉得不够合理.所以希望各位多多指教. 二. 什么是链 文章伊始,先让我们了解这个最基本的概念,什么是链. 我给链下了这样的定义: 1. 链是一系列节点的集合. 2. 链的各节点可灵活拆分再重组. 三. 何为职责链 职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这个对象连成一条

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

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于怎样创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化托付给还有一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些详细的类的信息封装起来.第二,它们隐藏了这些类的实例是怎样被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,