24.3职责链模式
职责链模式(Chain of Responsibility):使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
职责链模式(Chain of Responsibility)结构图
代码示例
Handler 类,定义一个处理请示的接口。
abstract class Handler{protected $successor = null;//设置继任者public function SetSuccessor($successor){$this->successor = $successor;}//处理请求的抽象方法abstract function HandleRequest($request);}
ConcreteHandler 类,具体处理者类,处理它所负责的请求,可访问它的后继者,如果可处理该请求,就处理之,否则就将该请求转发给它的后继者。
ConcreteHandler1,当请求数在 0 到 10 之间则有权处理,否则转到下一位。
class ConcreteHandler1 extends Handler{public function HandleRequest($request){//0 到 10,处理此请求if ($request >= 0 && $request < 10) {echo $request . ' 处理请求:' . __CLASS__ . PHP_EOL;} else if ($this->successor != null) {//转移到下一位$this->successor->HandleRequest($request);}}}
ConcreteHandler2,当请求数在 10 到 20 之间则有权处理,否则转到下一位。
class ConcreteHandler2 extends Handler{public function HandleRequest($request){//10 到 20,处理此请求if ($request >= 10 && $request < 20) {echo $request . ' 处理请求:' . __CLASS__ . PHP_EOL;} else if ($this->successor != null) {//转移到下一位$this->successor->HandleRequest($request);}}}
ConcreteHandler3,当请求数在 20 到 30 之间则有权处理,否则转到下一位。
class ConcreteHandler3 extends Handler{public function HandleRequest($request){//20 到 30,处理此请求if ($request >= 20 && $request < 30) {echo $request . ' 处理请求:' . __CLASS__ . PHP_EOL;} else if ($this->successor != null) {//转移到下一位$this->successor->HandleRequest($request);}}}
客户端代码,向链上的具体处理者对象提交请求。
public function chinaOfResponsibilityDemo(){$h1 = new ConcreteHandler1();$h2 = new ConcreteHandler2();$h3 = new ConcreteHandler3();//设置职责链上家与下家$h1->SetSuccessor($h2);$h2->SetSuccessor($h3);$request = array(2, 5, 14, 22, 18, 3, 27, 20);//循环给最小处理者提交请求,不同的数额,//由不同的权限处理者处理foreach ($request as $value) {$h1->HandleRequest($value);}}
24.4职责链的好处
职责链模式的好处:
- 当客户提交一个请求时,请求是沿链传递直至有一个 ConcreteHandler 对象负责处理它。
- 接收者和发送者都没有对方的明确信息,且链中的对象自己也并不知道链的结构。结果是职责链可简化对象的相互连接,他们仅需保持一个指向其后继者的引用,而不需保持它所有的候选接收者的引用。这也就大大降低了耦合度了。
- 由于是在客户端来定义链的结构,也就是说,可以随时地增加或修改处理一个请求的结构。增强了给对象指派职责的灵活性。
这的确是很灵活,不过也要当心,一个请求极有可能到了链的末端都得不到处理,或者因为没有正确配置而得不到处理,这就很糟糕。需要事先考虑全面。
就加薪的例子而言,最重要的有两点:
一个是你需要事先给每个具体管理者设置他的上司是哪个类,也就是设置后继者。
另一点是你需要在每个具体管理者处理请求时,做出判断,是可以处理这个请求,还是必须要‘推卸责任’,转移给后继者去处理。
24.5加薪代码重构
代码结构图
代码实现
管理者类
//管理者abstract class Manager{protected $name = '';//管理者的上级protected $superior = null;public function __construct($name){$this->name = $name;}//设置管理者的上级public function SetSuperior($superior){$this->superior = $superior;}//申请请求abstract function RequestApplications($request);}
经理类就可以去继承这个‘管理者’类,只需重写‘申请请求’的方法就可以了。
//经理class CommonManager extends Manager{public function __construct($name){parent::__construct($name);}public function RequestApplications($request){//经理所能有的权限就是可准许下属两天内的假期if ($request->getRequestType() == '请假' && $request->getNumber() <= 2) {echo $this->name . ':' . $request->getRequestContent() . ' 数量:' . $request->getNumber() . ' 被批准' . PHP_EOL;} else {//其余的申请都需转到上级if ($this->superior != null) {$this->superior->RequestApplications($request);}}}}
‘总监’类同样继承‘管理者类’。
//总监class Majordomo extends Manager{public function __construct($name){parent::__construct($name);}public function RequestApplications($request){//总监所能有的权限就是可准许下属一周内的假期if ($request->getRequestType() == '请假' && $request->getNumber() <= 5) {echo $this->name . ':' . $request->getRequestContent() . ' 数量:' . $request->getNumber() . ' 被批准' . PHP_EOL;} else {//其余的申请都需转到上级if ($this->superior != null) {$this->superior->RequestApplications($request);}}}}
‘总经理’的权限就是全部都需要处理。
//总经理class GeneralManager extends Manager{public function __construct($name){parent::__construct($name);}public function RequestApplications($request){//总经理可准许下属任意天的假期if ($request->getRequestType() == '请假') {echo $this->name . ':' . $request->getRequestContent() . ' 数量:' . $request->getNumber() . ' 被批准' . PHP_EOL;} else if ($request->getRequestType() == '加薪' && $request->getNumber() <= 500) { //加薪在500以内,没有问题echo $this->name . ':' . $request->getRequestContent() . ' 数量:' . $request->getNumber() . ' 被批准' . PHP_EOL;} else if ($request->getRequestType() == '加薪' && $request->getNumber() > 500) { //超过500,就要考虑一下了echo $this->name . ':' . $request->getRequestContent() . ' 数量:' . $request->getNumber() . ' 再说吧' . PHP_EOL;}}}
客户端代码
public function chainOfResponsibilityImp(){$jingli = new CommonManager('经理');$zongjian = new Majordomo('总监');$zongjingli = new GeneralManager('总经理');//设置上级,完全可以根据实际需求来更改设置$jingli->SetSuperior($zongjian);$zongjian->SetSuperior($zongjingli);//客户端申请都是由‘经理’发起,但实际谁来决策由具体管理类//来处理,客户端不知道$request = new Request();$request->setRequestType('请假');$request->setRequestContent('小菜请假');$request->setNumber(1);$jingli->RequestApplications($request);$request2 = new Request();$request2->setRequestType('请假');$request2->setRequestContent('小菜请假');$request2->setNumber(4);$jingli->RequestApplications($request2);$request3 = new Request();$request3->setRequestType('加薪');$request3->setRequestContent('小菜请求加薪');$request3->setNumber(500);$jingli->RequestApplications($request3);$request4 = new Request();$request4->setRequestType('加薪');$request4->setRequestContent('小菜请求加薪');$request4->setNumber(1000);$jingli->RequestApplications($request4);}
结果显示
经理:小菜请假 数量:1 被批准总监:小菜请假 数量:4 被批准总经理:小菜请求加薪 数量:500 被批准总经理:小菜请求加薪 数量:1000 再说吧
这的确是很好地解决了原来大量的分支判断造成难维护、灵活性差的问题。
