设计模式(十三)职责链

发布时间 2023-11-17 10:36:40作者: 冬先生

一、定义

避免将一个请求的发送者与接受者耦合在一起,让多个对象都有机会接受请求。将接收请求的对象连接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止。职责链模式是一种行为型模式

二、描述

职责链可以是一条直线、一个环或者一个树形结构,最常见的职责链是直线型,即沿着一条单向的链来传递请求。包含以下两个角色:
1、Handler(抽象处理者):它定义了一个处理请求的接口,一般设计为抽象类,由于不同的具体处理者处理请求的方式不同,因此在其中定义了抽象请求处理方法。每一个处理者的下家还是一个处理者,故在抽象处理者中定义了一个抽象处理者类型的对象(如结构图中的successor)作为其对下家的引用,通过该引用,处理者可以连成一条链。
2、ConcreteHandler(具体处理者):它是抽象处理者的子类,可以处理用户请求,在具体处理者类中实现了抽象处理者中定义的抽象请求处理方法,在处理请求之前需要进行判断,看是否有相应的处理权限,如果可以处理请求就处理它,否则则将请求转发给后继者;在具体处理者中可以访问链中的下一个对象,以便请求的转发。

三、例子

X公司承接了某企业SCM(Supply Chain Management,供应链管理)系统的开发任务,其中包含一个采购审批子系统。该企业的采购审批是分级进行的,即根据采购金额的不同由不同层次的主管人员来审批:主任可以审批5万元以下(不包括5万)的采购单,副董事长可以审批5万~10万(不包括10万)的采购单,50万元以及以上的采购单就需要开董事会讨论决定。PurchaseRequest:采购请求类

public class PurchaseRequest
{
    // 采购金额
    public double Amount { get; set; }
    // 采购单编号
    public string Number { get; set; }
    // 采购目的
    public string Purpose { get; set; }

    public PurchaseRequest(double amount, string number, string purpose)
    {
        Amount = amount;
        Number = number;
        Purpose = purpose;
    }
}

Approver:审批者类,充当抽象处理者

public 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);
}

Director、VicePresident、President、Congress:主管、副总裁、总裁、董事会,充当具体处理类

/// <summary>
/// 主管:具体处理类
/// </summary>
public 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);
        }
    }
}

/// <summary>
/// 副总裁:具体处理类
/// </summary>
public 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);
        }
    }
}

/// <summary>
/// 总裁:具体处理者
/// </summary>
public 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);
        }
    }
}

/// <summary>
/// 董事会:具体处理者
/// </summary>
public class Congress : Approver
{
    public Congress(string name) : base(name)
    {
    }

    // 具体请求处理方法
    public override void ProcessRequest(PurchaseRequest request)
    {
        // 处理请求
        Console.WriteLine("董事会 {0} 审批采购单:{1},金额:{2} 元,采购目的:{3}。",
            this.name, request.Number, request.Amount, request.Purpose);
    }
}

Program:客户端测试类

Approver andy = new Director("Andy");
Approver jacky = new VicePresident("Jacky");
Approver ashin = new President("Ashin");
Approver meeting = new Congress("Congress");

andy.SetSuccessor(jacky);
jacky.SetSuccessor(ashin);
ashin.SetSuccessor(meeting);

// 构造采购请求单并发送审批请求
PurchaseRequest request1 = new PurchaseRequest(45000.00, "MANULIFE201706001", "购买PC和显示器");
andy.ProcessRequest(request1);

PurchaseRequest request2 = new PurchaseRequest(60000.00, "MANULIFE201706002", "2017开发团队活动");
andy.ProcessRequest(request2);

PurchaseRequest request3 = new PurchaseRequest(160000.00, "MANULIFE201706003", "2017公司年度旅游");
andy.ProcessRequest(request3);

PurchaseRequest request4 = new PurchaseRequest(800000.00, "MANULIFE201706004", "租用新临时办公楼");
andy.ProcessRequest(request4);

Console.ReadLine();

四、总结

1、优点

(1)使得一个对象无需知道是其他哪一个对象处理其请求,仅需知道该请求会被处理即可,接收者和发送者都没有对方的明确信息,且链中的对象不需要知道链的结构,由客户端负责链创建,降低了系统的耦合度。
(2)请求处理对象仅需要维持一个指向其后继这的引用,而不需要维持它对所有的候选处理者的引用,可以简化对象之间的相互连接。
(3)在给对象分配职责时,职责链可以带来更多的灵活性,可以通过在运行时对该链进行动态的增加或修改来增加或改变处理一个请求的职责。
(4)在系统中增加一个新的具体处理者时无须修改原有系统源代码,只需要在客户端重新建立链式结构即可,从这一点来看是符合开闭原则的。

2、缺点

(1)由于一个请求没有明确的接受者,那么就不能保证它一定会被处理,该请求可能一直到链的末端都得不到处理;一个请求也可能因职责链没有被正确配置而得不到处理。
(2)对于比较长的职责链,请求的处理可能涉及多个处理对象,系统性能有一定影响且不利于调试。
(3)如果建立链不当,可能会造成循环调用,导致系统进入死循环。