理解和实现 C# 中的责任链模式






4.36/5 (9投票s)
本文讨论责任链模式。
引言
本文讨论责任链模式。我们将尝试了解何时可以使用此模式以及使用此模式的好处。我们还将介绍 C# 中责任链模式的一个基本实现。
背景
在观察者模式中,我们看到了一个类需要了解另一个类的状态变化的情况。在观察者模式中,一个类可以注册到另一个类,以便在其他类状态更改时获得通知。通知将以事件的形式出现,监听器类可以决定需要采取什么操作。
现在,如果我们进一步考虑这种情况,即监听事件的类将根据某些条件做出决策。如果条件不满足,它将把此事件传递给另一个可以处理此事件的对象。
因此,在这种情况下,我们有一系列对象可以根据某些标准处理事件。所有这些对象将按顺序将事件传递给其他对象。该类是否可以采取行动,或者它需要进一步发送事件,这是该类将包含的逻辑。所有可以处理事件的类都链接在一起,即每个类都包含指向其后继者的句柄,它可以将事件传递给它。
责任链模式就是为了应对这种情况而设计的。在这种模式下,一个对象会监听一个事件,当事件发生时,它会处理该事件。如果该对象有能力采取行动,它将采取行动,否则它将把事件传播给链中的另一个对象,该对象可以处理该事件或进一步传播它。此模式在实现工作流类场景方面非常有用。
GoF 将责任链模式定义为“通过让多个对象有机会处理请求,避免将请求的发送者与其接收者耦合。将接收对象链接起来,并通过链传递请求,直到某个对象处理它。”

为了理解这个类图,我们来看看每个类。
-
Handler
:这是所有可以处理事件的类都应该实现的接口或抽象类。 -
ConcreteHandler
:这些类能够处理事件或将其进一步传播。 -
Client
:这是定义链的类,即所有ConcreteHandlers
的后继者,也是发起请求到ConcreteHandler
的类。
使用代码
为了更好地理解这个模式,让我们尝试实现一个简单的基础工作流应用程序。
假设我们有一个组织,团队成员请假时,请求会发送给团队负责人。团队负责人可以批准所有不到 10 天的请假请求。如果请假超过 10 天,则请求将转交给项目负责人。项目负责人最多可以批准 20 天的假期。如果请假超过 20 天,则此请求将转交给人力资源部。人力资源部最多可以批准 30 天的假期。如果请假超过 30 天,则请假申请无法由系统批准,需要手动流程批准。
现在,根据上述要求,如果我们尝试实现责任链模式,我们将需要团队负责人、项目负责人和人力资源部的 ConcreteHandlers
。这些 ConcreteHandlers
将被链接在一起,以便请求可以从 TeamLeader
传递到 ProjectLeader
再到 HR
。
此外,我们需要一个抽象类来包含通用功能,例如跟踪后继对象、发起请求和事件机制。我们将此类命名为 Employee
。ConcreteHandler
将包含特定于具体处理程序的逻辑。
让我们开始查看 Handler
抽象类,即 Employee
类。
public abstract class Employee
{
// Every employee will have a supervisor
protected Employee supervisor;
// Event mechanism to know whenever a leave has been applied
public delegate void OnLeaveApplied(Employee e, Leave l);
public event OnLeaveApplied onLeaveApplied = null;
// This will invoke events when the leave will be applied
// i.e. the actual item will be handed over to the hierarchy of
// concrete handlers.
public void LeaveApplied(Employee s, Leave leave)
{
if (onLeaveApplied != null)
{
onLeaveApplied(this, leave);
}
}
// This is the function which concrete handlers will use to take
// action, if they are able to take actions.
public abstract void ApproveLeave(Leave leave);
// getter to get the supervisor of current employee
public Employee Supervisor
{
get
{
return supervisor;
}
set
{
supervisor = value;
}
}
// Using this we can apply for leave
public void ApplyLeave(Leave l)
{
LeaveApplied(this, l);
}
}
此 Handler
类负责:
- 跟踪此对象的后继者
- 实现事件机制以通知和传播请求事件。
- 发起请求。
现在我们的 Handler 类已经准备好了,让我们一个一个地看看 ConcreteHandlers
。让我们从 TeamLeader
类开始。
public class TeamLeader : Employee
{
// team leas can only approve upto 7 days of leave
const int MAX_LEAVES_CAN_APPROVE = 10;
// in constructor we will attach the event handler that
// will check if this employee can process or he need to
// pass on to next employee
public TeamLeader()
{
this.onLeaveApplied += new OnLeaveApplied(TeamLeader_onLeaveApplied);
}
// in this function we will check if this employee can
// process or he need to pass on to next employee
void TeamLeader_onLeaveApplied(Employee e, Leave l)
{
// check if we can process this request
if (l.NumberOfDays < MAX_LEAVES_CAN_APPROVE)
{
// process it on our level only
ApproveLeave(l);
}
else
{
// if we cant process pass on to the supervisor
// so that he can process
if (Supervisor != null)
{
Supervisor.LeaveApplied(this, l);
}
}
}
// If we can process lets show the output
public override void ApproveLeave(Leave leave)
{
Console.WriteLine("LeaveID: {0} Days: {1} Approver: {2}",
leave.LeaveID, leave.NumberOfDays, "Team Leader");
}
}
此类所做的事情是:
- 检查此类是否可以采取行动,即请假天数是否少于 10 天。
- 如果此类可以采取行动,则向用户显示响应。
- 如果此类无法采取行动,则将请求传递给
ProjectLeader
类,即其后继者。
现在让我们看看 ProjectLeader
类。
class ProjectLeader : Employee
{
const int MAX_LEAVES_CAN_APPROVE = 20;
// in constructor we will attach the event handler that
// will check if this employee can process or he need to
// pass on to next employee
public ProjectLeader()
{
this.onLeaveApplied += new OnLeaveApplied(ProjectLeader_onLeaveApplied);
}
// in this function we will check if this employee can
// process or he need to pass on to next employee
void ProjectLeader_onLeaveApplied(Employee e, Leave l)
{
// check if we can process this request
if (l.NumberOfDays < MAX_LEAVES_CAN_APPROVE)
{
// process it on our level only
ApproveLeave(l);
}
else
{
// if we cant process pass on to the supervisor
// so that he can process
if (Supervisor != null)
{
Supervisor.LeaveApplied(this, l);
}
}
}
// If we can process lets show the output
public override void ApproveLeave(Leave leave)
{
Console.WriteLine("LeaveID: {0} Days: {1} Approver: {2}",
leave.LeaveID, leave.NumberOfDays, "Project Leader");
}
}
此类所做的事情是:
- 检查此类是否可以采取行动,即请假天数是否少于 20 天。
- 如果此类可以采取行动,则向用户显示响应。
- 如果此类无法采取行动,则将请求传递给
HR
类,即其后继者。
现在让我们看看 HR
类。
class HR : Employee
{
const int MAX_LEAVES_CAN_APPROVE = 30;
// in constructor we will attach the event handler that
// will check if this employee can process or
// some other action is needed
public HR()
{
this.onLeaveApplied += new OnLeaveApplied(HR_onLeaveApplied);
}
// in this function we will check if this employee can
// process or some other action is needed
void HR_onLeaveApplied(Employee e, Leave l)
{
// check if we can process this request
if (l.NumberOfDays < MAX_LEAVES_CAN_APPROVE)
{
// process it on our level only
ApproveLeave(l);
}
else
{
// if we cant process pass on to the supervisor
// so that he can process
if (Supervisor != null)
{
Supervisor.LeaveApplied(this, l);
}
else
{
// There is no one up in hierarchy so lets
// tell the user what he needs to do now
Console.WriteLine("Leave application suspended, Please contact HR");
}
}
}
// If we can process lets show the output
public override void ApproveLeave(Leave leave)
{
Console.WriteLine("LeaveID: {0} Days: {1} Approver: {2}",
leave.LeaveID, leave.NumberOfDays, "HR");
}
}
此类所做的事情是:
- 检查此类是否可以采取行动,即请假天数是否少于 30 天。
- 如果此类可以采取行动,则向用户显示响应。
- 如果此类无法采取行动,则告知用户需要进行手动讨论,并且其请求已被暂停。
实际的操作项,即请假,被封装在一个类中,以提高模块化程度,因此在运行应用程序之前,让我们看看这个 Leave
类是什么样的。
// This is the actual Item that will be used by the concretehandlers
// to determine whther they can act upon this request or not
public class Leave
{
public Leave(Guid guid, int days)
{
leaveID = guid;
numberOfDays = days;
}
Guid leaveID;
public Guid LeaveID
{
get { return leaveID; }
set { leaveID = value; }
}
int numberOfDays;
public int NumberOfDays
{
get { return numberOfDays; }
set { numberOfDays = value; }
}
}
最后,我们需要 Client
来设置后继链并发起请求。
class Program
{
static void Main(string[] args)
{
// lets create employees
TeamLeader tl = new TeamLeader();
ProjectLeader pl = new ProjectLeader();
HR hr = new HR();
// Now lets set the hierarchy of employees
tl.Supervisor = pl;
pl.Supervisor = hr;
// Now lets apply 5 day leave my TL
tl.ApplyLeave(new Leave(Guid.NewGuid(), 5));
// Now lets apply 15 day leave my TL
tl.ApplyLeave(new Leave(Guid.NewGuid(), 15));
// Now lets apply 25 day leave my TL
tl.ApplyLeave(new Leave(Guid.NewGuid(), 25));
// Now lets apply 35 day leave my TL
tl.ApplyLeave(new Leave(Guid.NewGuid(), 35));
Console.ReadLine();
}
}
因此,我们可以看到这个 Main 函数通过为每个类设置后继者来创建链,并向 TeamLeader
类发起请求。每种场景都发起了一个请求。当我们运行此应用程序时。

因此,我们可以看到每个请求都根据申请的天数被相应的类传递和处理。在总结之前,让我们看一下我们应用程序的类图,并将其与原始 GoF 图进行比较。

关注点
在本文中,我们尝试探讨了责任链模式。我们看到了何时可以使用此模式,使用此模式的好处以及如何使用 C# 对此模式进行基本的粗略实现。本文是从初学者的角度编写的。希望本文内容丰富。
历史
- 2012年11月16日:初版