一:动机(Motivation)
<1>在软件构建过程中,一个请求可能被多个对象处理,但是每个请求在运行时只能有一个接受者,如果显式指定,将必不可少地带来请求发送者与接受者的紧耦合。
<2>如何使请求的发送者不需要指定具体的接受者?让请求的接受者自己在运行时决定来处理请求,从而使两者解耦。
二:意图(Intent)
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
——《设计模式》GoF
三:结构(Structure)
四:结构详解
五:生活中的例子
员工向主管上级提出申请
申请有两种:请假和加薪
职责链是:总监>经理>总经理
总监能处理:请假,且在2小时以内
经理能处理:请假,且在5小时以内
总经理能处理:
请假:小时不限,都通过
加薪:
1000元以内批准
1000元以上要考察再说
六:实现
namespace Test
{
public enum 申请类型
{
请假,
加薪
}
/// <summary>
/// 实现申请
/// </summary>
public class 申请
{
public 申请类型 类型;
public string 内容;
public int 数量;
public override string ToString()
{
return string.Format("内容:{0},类型:{1},数量:{2}", this.内容, this.类型, this.数量);
}
}
/// <summary>
/// 实现管理者
/// </summary>
public abstract class 管理者
{
protected string name;
public 管理者(string 姓名)
{
this.name = 姓名;
}
protected 管理者 superior;
public 管理者 上级主管
{
get { return this.superior; }
set { this.superior = value; }
}
public abstract void 处理员工申请(申请 request);
}
/// <summary>
/// 实现总监
/// </summary>
public class 总监 : 管理者
{
public 总监(string 姓名) : base(姓名) { }
public override void 处理员工申请(申请 request)
{
if (request.类型 == 申请类型.请假 && request.数量 <= 2)
{
Console.WriteLine("总监:{0},通过 {1} 申请,数量:{2}", base.name, request.内容, request.数量);
}
else
{
if (base.superior != null)
{
Console.WriteLine("总监:{0},无权处理 {1} 申请,数量:{2},转交给上级", base.name, request.内容, request.数量);
base.superior.处理员工申请(request);
}
}
}
}
/// <summary>
/// 实现经理
/// </summary>
public class 经理 : 管理者
{
public 经理(string 姓名) : base(姓名) { }
public override void 处理员工申请(申请 request)
{
if (request.类型 == 申请类型.请假 && request.数量 <= 5)
{
Console.WriteLine("经理:{0},通过 {1} 申请,数量:{2}", base.name, request.内容, request.数量);
}
else
{
if (base.superior != null)
{
Console.WriteLine("经理:{0},无权处理 {1} 申请,数量:{2},转交给上级", base.name, request.内容, request.数量);
base.superior.处理员工申请(request);
}
}
}
}
/// <summary>
/// 实现总经理
/// </summary>
public class 总经理 : 管理者
{
public 总经理(string 姓名) : base(姓名) { }
public override void 处理员工申请(申请 request)
{
if (request.类型 == 申请类型.请假)
{
Console.WriteLine("总经理:{0},通过 {1} 申请,数量:{2}", base.name, request.内容, request.数量);
return;
}
else
{
if (request.类型 == 申请类型.加薪 && request.数量 <= 1000)
{
Console.WriteLine("总经理:{0},无权处理 {1} 申请,数量:{2}", base.name, request.内容, request.数量);
return;
}
else
{
Console.WriteLine("总经理:{0},没有通过 {1} 申请,数量:{2},再考察一下工作表现再说吧", base.name, request.内容, request.数量);
return;
}
}
}
}
internal class Program
{
static void Main(string[] args)
{
申请 request = new 申请();
request.类型 = 申请类型.请假;
request.数量 = 1;
request.内容 = "小李要请假";
管理者 张三 = new 总监("张三");
管理者 李四 = new 经理("李四");
管理者 王五 = new 总经理("王五");
张三.上级主管 = 李四;
李四.上级主管 = 王五;
Console.WriteLine("\n第1次申请:{0}", request);
张三.处理员工申请(request);
request.数量 = 8;
Console.WriteLine("\n第2次申请:{0}", request);
张三.处理员工申请(request);
request.类型 = 申请类型.加薪;
request.内容 = "小李要涨工资";
request.数量 = 800;
Console.WriteLine("\n第3次申请:{0}", request);
张三.处理员工申请(request);
request.数量 = 1500;
Console.WriteLine("\n第4次申请:{0}", request);
张三.处理员工申请(request);
Console.ReadLine();
}
}
}
实现-结果
七:实现要点
<1>链的顺序可灵活改变-改变”上级主管”即可;
<2>发起请求者只需要将请求发送给”最近”的上级,即总监,不需要关心后续的链接关系。
八:适用性
<1>系统已经有一个由处理者对象组成的链。这个链可能由复合模式给出;
<2>当有多于一个的处理者对象会处理一个请求,而且在事先并不知道到底由哪一个处理者对象处理一个请求。这个处理者对象是动态确定的;
<3>当系统想发出一个请求给多个处理者对象中的某一个,但是不明显指定是哪一个处理者对象会处理此请求;
<4>当处理一个请求的处理者对象集合需要动态地指定时。
九:总结
<1>Chain of Responsibility 模式的应用场合在于“一个请求可能有多个接受者,但是最后真正的接受者只有一个”,只有这时候请求发送者与接受者的耦合才有可能出现“变化脆弱”的症状,职责链的目的就是将二者解耦,从而更好地应对变化。
<2>应用了Chain of Responsibility 模式后,对象的职责分派将更具灵活性。我们可以在运行时动态添加/修改请求的处理职责。
<3>如果请求传递到职责链的末尾仍得不到处理,应该有一个合理的缺省机制。这也是每一个接受对象的责任,而不是发出请求的对象的责任。