动机(Motivation)
在软件构建过程中,一个请求可能被多个对象处理,但是每个请求在运行时只能有一个接受者,如果显示指定,将必不可少地带来请求发送者与接受者的紧耦合。
如何使请求的发送者不需要指定具体的接受者?让请求的接受者自己在运行时决定来处理请求,从而让两者解耦。
意图(Intent)
使多个对象有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
结构(Structure)
其中,
Handler:定义一个处理请示的接口
ConcerteHandler1:具体处理者类,处理它所负责的请求,可访问它的后继者,如果可处理该请求,就处理之,否则就将该请求转发给它的后继者
代码实现
实例背景:在公司,请假和加薪都要有各级审批制度,审批权限如下。(假设只涉及3个管理者:经理,总监和总经理)
申请请假:2天以内,经理审批。5天以内,总监有权审批。总经理可审批任何天数。
申请加薪:经理和总监无权。加薪500以内总经理批准,500-1000,总经理还需要在考虑是否加薪。
把处理员工申请的流程可以看做一个职责链,无论是请假还是加薪,都先由经理进行判断,经理若有权处理,则处理之,无权处理转交给他的上级(继任者),也就是总监。总监再次判断自己是否有权处理,有权处理,无权转交其上级(继任者)-总经理处理。
申请类:有RequestType申请类别,RequestContent申请内容和Number申请数量这三个字段属性。
class Request
{
//申请类别
private string requestType;
public string RequestType
{
get { return requestType; }
set { requestType = value; }
}
//申请内容 同上...
//数量 同上...
}
管理者:对应于结构图中的Handler类,定义一个处理请示的接口,本实例中,定义了一个处理请假和审批的抽象方法RequestApplications(Request request)
abstract class Manager
{
protected string name;
//管理者的上级
protected Manager superior;
public Manager(string name)
{
this.name = name;
}
//设置管理者的上级
public void SetSuperior(Manager superior)
{
this.superior = superior;
}
//申请请求
abstract public void RequestApplications(Request request);
}
经理类、总监类、总经理类:对应于结构图中的ConcreteHandler类,只需要重写“申请请求RequestApplications(Request request)”的方法就可以了
经理的权限是处理2天内的请假,其余申请转交给上级去处理
//经理类
class CommonManager : Manager
{
public CommonManager(string name):base(name) { }
public override void RequestApplications(Request request)
{
if (request.RequestType=="请假" && request.Number<=2)
{
Console.WriteLine("{0}:{1}数量{2}被批准",name,request.RequestContent,request.Number);
}
else
{
if (superior != null)
superior.RequestApplications(request);
}
}
}
总监类的代码和总经理类的代码与经理类代码类似,只是处理权限不同,总监所能有的权限就是可准许下属5天内的假期,其余申请都需要转到上级。总经理可以准许下属任意天的假期和判断是否可以加薪。
客户端代码
实例化出三个管理者:经理小王,总监小刘,总经理小张
CommonManager jingli = new CommonManager("经理小王");
Majordomo zongjian = new Majordomo("总监小刘");
GeneralManager zongjingli = new GeneralManager("总经理小张");
设置上级:在这里,设置经理的上级是总监,总监的上级是总经理(可根据实际要求进行调整,较为灵活)
jingli.SetSuperior(zongjian);
zongjian.SetSuperior(zongjingli);
第1个申请:申请类型:请假,申请内容:“小菜请假”,数量:1天
Request request1 = new Request();
request1.RequestType = "请假";
request1.RequestContent = "小菜请假";
request1.Number = 1;
jingli.RequestApplications(request1);
第2个申请: 申请类型:请假,申请内容:小菜请假,数量:4天
Request request2 = new Request();
request2.RequestType = "请假";
request2.RequestContent = "小菜请假";
request2.Number = 4;
jingli.RequestApplications(request2);
可以看到客户端的申请都是由“经理”发起,但实际谁来决策由具体管理类来处理,客户端并不知道,加薪的请求也是一样,可参考上面请假代码
程序运行结果
Chian of Responsibility模式的几个要点
1.当客户提交一个请求时,请求是沿链传递直至有一个ConcreteHandler对象负责处理它。
2.接受者和发送者都没有对方的明确消息,且链中的对象自己也并不知道链的结构,结果是职责链可以简化对象的相互连接,他们仅需保持一个指向其后继用者的引用,而不需要保持它所有的候选接受者的引用。
3.如果请求传递到职责链的末尾仍得不到处理,应该有一个合理的缺省机制。这也是每一个接受对象的责任,而不是发出请求的对象的责任。