1. 职责链模式简介
在软件构建过程中,一个请求可能被多个对象处理,但是每个请求在运行时只能有一个接受者,如果显式指定,将必不可少地带来请求发送者与接受者之间的紧耦合。
如何使请求的发送者不需要指定具体的接受者?让请求的接受者在运行时决定谁来处理请求,从而使两者解耦。
职责链可以是一条直线、一个环或者一个树形结构,最常见的职责链是直线型,即沿着一条单向的链来传递请求。
避免将一个请求的发送者与接收者耦合在一起,让多个对象都有机会处理请求。将接收请求的对象连接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止。
——《设计模式》GoF
2. 结构
- 抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义出一个方法,以设定和返回对下家的引用。这个角色通常由一个抽象类或接口实现。
- 具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。
典型的抽象处理者代码:
abstract class Handler{
//维持对下家的引用
protected Handler successor;
public void SetSuccessor(Handler successor){this.successor = successor;}
public abstract void HandleRequest(string request);
}
典型的具体处理者代码:
class ConcreteHandler : Handler {
public override void HandleRequest(string request){
if (请求满足条件){
//处理请求
}
else{
this.successor.HandleRequest(request); //转发请求
}
}
}
典型的客户端代码:
……
Handler handler1, handler2, handler3;
handler1 = new ConcreteHandlerA();
handler2 = new ConcreteHandlerB();
handler3 = new ConcreteHandlerC();
//创建职责链
handler1.SetSuccessor(handler2);
handler2.SetSuccessor(handler3);
//发送请求,请求对象通常为自定义类型
handler1.HandleRequest("请求对象");
……
3. 示例
某企业的SCM(Supply Chain Management,供应链管理)系统中包含一个采购审批子系统。该企业的采购审批是分级进行的,即根据采购金额的不同由不同层次的主管人员来审批,主任可以审批5万元以下(不包括5万元)的采购单,副董事长可以审批5万元至10万元(不包括10万元)的采购单,董事长可以审批10万元至50万元(不包括50万元)的采购单,50万元及以上的采购单就需要开董事会讨论决定。
现使用职责链模式设计并实现该系统。
示例代码
(1) PurchaseRequest:采购单类,充当请求类
(2) Approver:审批者类,充当抽象处理者
(3) Director:主任类,充当具体处理者
(4) VicePresident:副董事长类,充当具体处理者
(5) President:董事长类,充当具体处理者
(6) Congress:董事会类,充当具体处理者
(7) Program:客户端测试类
namespace CoRSample{
/// 采购请求--具体处理请求--封装与请求相关的数据
class PurchaseRequest{
private double amount; //采购金额
private int number; //采购单编号
private string purpose; //采购目的
public PurchaseRequest(double amount, int number, string purpose){
this.amount = amount;
this.number = number;
this.purpose = purpose;
}
public double Amount{
get { return amount; }
set { amount = value; }
}
public int Number{
get { return number; }
set { number = value; }
}
public string Purpose{
get { return purpose; }
set { purpose = value; }
}
}
}
namespace CoRSample{
/// 抽象处理者
abstract class Approver{
protected Approver successor; //定义后继对象
protected string name; //审批者姓名
public Approver(string name){this.name = name;}
//设置后继者
public void SetSuccessor(Approver successor){
this.successor = successor;
}
//抽象请求处理方法
public abstract void ProcessRequest(PurchaseRequest request);
}
}
namespace CoRSample{
/// 董事会--具体处理者
class Congress : Approver{
public Congress(string name) : base(name){}
//具体请求处理方法
public override void ProcessRequest(PurchaseRequest request) {
Console.WriteLine("召开董事会审批采购单:{0},金额:{1}元,采购目的:{2}。",request.Number, request.Amount, request.Purpose); //处理请求
}
}
}
namespace CoRSample{
/// 主任--具体处理者
class Director : Approver{
public Director(string name) : base(name){}
//具体请求处理方法
public override void ProcessRequest(PurchaseRequest request) {
if (request.Amount < 50000) {
Console.WriteLine("主任{0}审批采购单:{1},金额:{2}元,采购目的:{3}。", this.name, request.Number, request.Amount, request.Purpose); //处理请求
}
else {
this.successor.ProcessRequest(request); //转发请求
}
}
}
}
namespace CoRSample{
/// 经理--具体处理者
class Manager : Approver{
public Manager(string name) : base(name){}
//具体请求处理方法
public override void ProcessRequest(PurchaseRequest request) {
if (request.Amount < 80000) {
Console.WriteLine("经理{0}审批采购单:{1},金额:{2}元,采购目的:{3}。", this.name, request.Number, request.Amount, request.Purpose); //处理请求
}
else {
this.successor.ProcessRequest(request); //转发请求
}
}
}
}
namespace CoRSample{
/// 董事长--具体处理者
class President : Approver{
public President(string name) : base(name){}
//具体请求处理方法
public override void ProcessRequest(PurchaseRequest request) {
if (request.Amount < 500000) {
Console.WriteLine("董事长{0}审批采购单:{1},金额:{2}元,采购目的:{3}。", this.name, request.Number, request.Amount, request.Purpose); //处理请求
}
else{
this.successor.ProcessRequest(request); //转发请求
}
}
}
}
namespace CoRSample{
/// 副董事长--具体处理者
class VicePresident : Approver{
public VicePresident(string name) : base(name){}
//具体请求处理方法
public override void ProcessRequest(PurchaseRequest request) {
if (request.Amount < 100000) {
Console.WriteLine("副董事长{0}审批采购单:{1},金额:{2}元,采购目的:{3}。", this.name, request.Number, request.Amount, request.Purpose); //处理请求
}
else {
this.successor.ProcessRequest(request); //转发请求
}
}
}
}
namespace CoRSample{
class Program{
/// 客户端程序
/// <param name="args"></param>
static void Main(string[] args){
Approver wjzhang, gyang, jguo, meeting,manager;
//创建具体处理者
wjzhang = new Director("张无忌");
gyang = new VicePresident("杨过");
jguo = new President("郭靖");
meeting = new Congress("董事会");
manager=new Manager("张三");
//创建职责链----职责链的顺序取决于条件的判断方式
wjzhang.SetSuccessor(manager);
manager.SetSuccessor(gyang);
gyang.SetSuccessor(jguo);
jguo.SetSuccessor(meeting);
//创建采购单
PurchaseRequest pr1 = new PurchaseRequest(45000, 10001, "购买倚天剑");
wjzhang.ProcessRequest(pr1);
PurchaseRequest pr2 = new PurchaseRequest(60000, 10002, "购买《葵花宝典》");
wjzhang.ProcessRequest(pr2);
PurchaseRequest pr3 = new PurchaseRequest(160000, 10003, "购买《金刚经》");
wjzhang.ProcessRequest(pr3);
PurchaseRequest pr4 = new PurchaseRequest(800000, 10004, "购买桃花岛");
wjzhang.ProcessRequest(pr4);
PurchaseRequest pr5 = new PurchaseRequest(90000, 10005, "xxxxxx");
wjzhang.ProcessRequest(pr5);
Console.Read();
}
}
}
4. 分析与总结
职责链模式优点:
-
使得一个对象无须知道是其他哪一个对象处理其请求,降低了系统的耦合度;
-
可简化对象之间的相互连接;
-
给对象职责的分配带来更多的灵活性;
-
增加一个新的具体请求处理者时无须修改原有系统的代码,只需要在客户端重新建链即可
职责链模式缺点:
- 不能保证请求一定会被处理;
- 对于比较长的职责链,系统性能将受到一定影响,在进行代码调试时不太方便;
- 如果建链不当(SetSuccessor使用不当),可能会造成循环调用,将导致系统陷入死循环。
职责链模式适用环境:
- 有多个对象可以处理同一个请求,具体哪个对象处理该请求待运行时刻再确定;
- 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求;
- 可动态指定一组对象处理请求。