问题:
在面向对象的设计中,经常会遇到有多个对象共同处理同一事件,但是各自对事件的处理权限却有不同的场合,一个比较简单的例子就是逐级审批,组长审批完成后,交给经理审批,经理审批完成交给总监,总监审批完给总裁...比较传统的解决办法就是,由客户端来判断,现在处于哪个阶段,下一步应调用哪个对象的方法,进入哪一个阶段,可是请求的发送者需要维护多个耦合,以处理请求信息的传达。这就存在请求的发送者与多个接收者之间是高耦合。
定义:
使多个对象都有机会处理请求,从而避免请求的发送者和接收者的耦合关系,将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理了它为止。
意图:
Chain of Responsibility模式中ConcreteHandler将自己的后继对象(向下传递消息的对象)记录在自己的后继表中,通过这样的连接,将可能处理一个请求的对象链接成一个链,当一个请求到来时,ConcreteHandler会先检查看自己有没有匹配的处理程序,如果有就自己处理,否则传递给它的后继。这使得发出这个请求的客户端并不需要知道链上的哪一个对象最终处理这个请求,避免了请求的发出者与接收者之间的高耦合,客户端只需组织其责任链,并且设置请求,并可以动态地重新组织和分配责任。
参与者:
•抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义出一个方法,以设定和返回对后继对象的引用。这个角色通常由一个抽象类或接口实现。
•具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给后继对象。由于具体处理者持有对后继对象的引用,因此,如果需要,具体处理者可以访问后继对象。
UML:
代码说明:
/// 需要处理的对象
/// </summary>
public class Request
{
public int State { get; set; }
}
/// <summary>
/// 抽象处理者(Handler)角色
/// </summary>
public abstract class Handler
{
/// <summary>
/// 设置后继者
/// </summary>
/// <param name="nextHandler"></param>
public void SetNextHandler(Handler nextHandler)
{
this.NextHandler = nextHandler;
}
public Handler NextHandler { get; set; }
/// <summary>
/// 处理请求
/// </summary>
/// <param name="request"></param>
public abstract void HandlerRequest(Request request);
}
/// <summary>
/// 具体处理者(ConcreteHandler)角色
/// </summary>
public class ConcreteHandlerA : Handler
{
public override void HandlerRequest(Request request)
{
if (request.State == 0)
{
request.State++;
System.Console.WriteLine("已被ConcreteHandlerA处理");
}
else
{
System.Console.WriteLine("已将请求转给Next Handler");
this.NextHandler.HandlerRequest(request);
}
}
}
/// <summary>
/// 具体处理者(ConcreteHandler)角色
/// </summary>
public class ConcreteHandlerB : Handler
{
public override void HandlerRequest(Request request)
{
if (request.State == 1)
{
request.State++;
System.Console.WriteLine("已被ConcreteHandlerB处理");
}
else
{
System.Console.WriteLine("已将请求转给Next Handler");
this.NextHandler.HandlerRequest(request);
}
}
}
/// <summary>
/// 具体处理者(ConcreteHandler)角色
/// </summary>
public class ConcreteHandlerC : Handler
{
public override void HandlerRequest(Request request)
{
if (request.State == 2)
{
request.State++;
System.Console.WriteLine("已被ConcreteHandlerC处理");
}
else
{
System.Console.WriteLine("已将请求转给Next Handler");
this.NextHandler.HandlerRequest(request);
}
}
}
/// <summary>
/// 客户端测试
/// </summary>
public void ChainOfResponsibilityTest()
{
//定义责任链
Handler concreteHandlerA = new ConcreteHandlerA();
Handler concreteHandlerB = new ConcreteHandlerB();
concreteHandlerB.SetNextHandler(concreteHandlerA);
Handler concreteHandlerC = new ConcreteHandlerC();
concreteHandlerC.SetNextHandler(concreteHandlerB);
//初始化对象发出请求
Request request = new Request();
concreteHandlerC.HandlerRequest(request);
}
优点:
•责任链模式降低了请求的发送端和接收端之间的耦合,使多个对象都有机会处理这个请求。
•由于是在客户端来定义责任链的结构,可以动态地增加或修改处理一个请求的结构,增强了给对象指派职责的灵活性。
缺点:
•责任链模式一般是从链子的开头位置进行遍历,找到时候的处理对象,对性能有一定的损耗。
适用场合:
•有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
•想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
•可处理一个请求的对象集合应被动态指定。
•当一个方法的传入参数将成为分支语句的判断条件,分支条件存在扩展的可能,每一个分支的职责相对独立,且逻辑较为复杂时。
与状态模式的区别:
责任链模式注重责任的传递,并且责任链由客户端进行配置,具体的操作对象维护了一下后继者的引用(由客户端配置),如果自己能够处理,则处理,不能处理调用后继者处理。
状态模式注重对象状态的转换,这个转换过程对客户端是透明的。重点是把状态的判断逻辑封装在“表示不同状态的一系列类中”具体的操作完成后,修改被操作这的状态,这个状态的修改对客户端是透明的。
PS:
•纯责任链模规定一个具体的处理对象只能对请求作出处理请求或传给后继者两种动作,不能出现处理了一部分,把剩下的传给后继者处理的情况,而且请求在责任链中必须被处理。然而责任链模的思想在于,通过将多个处理对象建立关联,来达到请求与具体的某个处理者的解耦。所以在实际的需求中不一定非要达到一种纯责任链模式的设计。
•责任链模式并不创建责任链。责任链的创建必须由系统的其它部分创建出来。
•责任链可以是一个链可以是一条线,一个树,也可以是一个环。
•GoF规定一个请求必须被某一个处理者对象所处理,所以为了避免一个请求有可能到了链的末端都得不到处理,或者因为没有正确配置而得不到处理,可以提供一个默认处理所有请求的对象。