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 再说吧
这的确是很好地解决了原来大量的分支判断造成难维护、灵活性差的问题。