【愚公系列】2023年10月 二十三种设计模式(十三)-职责链模式(Chain of Responsibility Patter
🏆 作者简介,愚公搬代码
🏆《头衔》:华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,阿里云专家博主,腾讯云优秀博主,掘金优秀博主,51CTO博客专家等。
🏆《近期荣誉》:2022年CSDN博客之星TOP2,2022年华为云十佳博主等。
🏆《博客内容》:.NET、Java、Python、Go、Node、前端、IOS、Android、鸿蒙、Linux、物联网、网络安全、大数据、人工智能、U3D游戏、小程序等相关领域知识。
🏆🎉欢迎 👍点赞✍评论⭐收藏
🚀前言
设计模式(Design Pattern)是软件开发领域的宝贵经验,是多人反复借鉴和广泛应用的代码设计指导。它们是一系列经过分类和归纳的代码组织方法,旨在实现可重用性、可维护性和可理解性。使用设计模式,我们能够编写高质量的代码,使其更易于他人理解,并提供了代码可靠性的保证。
毫无疑问,设计模式对个人、团队和整个系统都带来了显著的益处。它们将代码开发提升到工程化水平,为软件工程构建提供了坚实的基础,就如同大厦的一块块精巧的砖石一样。在项目中明智地应用设计模式可以完美地解决各种复杂问题。每种设计模式都有相应的原理和最佳实践,它们描述了我们日常开发中不断遇到的问题,以及这些问题的核心解决方法。正是因为这种实用性和通用性,设计模式才能在软件开发中广泛地得以应用。设计模式是构建稳健、可扩展和可维护软件的关键工具,为开发者们提供了解决问题的智慧和指导。
🚀一、职责链模式(Chain of Responsibility Pattern)
职责链模式是一种行为型设计模式,旨在将请求发送者与接收者解耦。它通过创建一个接收者对象链来处理请求,其中每个接收者都可以决定是否处理请求,如果不能处理,则将请求传递给下一个接收者。通常有两种主要实现方式:
第一种实现方式是每个接收者都直接包含对下一个接收者的引用。这样,在接收者不能处理请求时,可以直接将请求传递给下一个接收者。这种方式相对简单,但可能会导致紧密耦合的接收者链。
第二种实现方式是引入一个中间链条类,通常称为中间件或处理器。每个中间件对象负责决定是否处理请求,并决定是否将请求传递给下一个中间件。这种方式更加灵活,允许动态配置和扩展处理链。本例采用了第二种实现方式。
职责链模式是一种有助于实现松耦合的设计模式,通过创建一个请求处理链,可以轻松地扩展和修改请求的处理方式,从而增强了系统的灵活性和可维护性。
🚀二、使用步骤
🔎1.角色
🦋1.1 抽象处理者(Handler)
职责链模式(Chain of Responsibility Pattern)中的抽象处理者(Handler)是该模式的核心概念,它扮演了以下关键角色和作用:
定义接口:抽象处理者定义了一个处理请求的接口或抽象方法,通常包括一个处理方法(例如
handleRequest
)。这个接口规定了所有具体处理者必须实现的方法。维护后继者:抽象处理者通常包含一个指向下一个处理者的引用,这样它可以将请求传递给下一个处理者。这一点是实现职责链的关键,它允许多个处理者形成一个链式结构,依次处理请求。
处理请求:抽象处理者可以尝试处理请求,如果它能够处理请求,则执行相应的处理逻辑;如果不能处理请求,它会将请求传递给下一个处理者,从而实现请求的传递和分发。
解耦发送者和接收者:抽象处理者的作用之一是将请求的发送者与接收者解耦。发送者不需要知道具体哪个处理者会处理请求,而只需将请求发送给第一个处理者即可。
可扩展性:抽象处理者的存在使得系统具有高度的可扩展性。新的处理者可以很容易地添加到链中,而不需要修改已有的代码。
抽象处理者在职责链模式中起到了定义标准接口、协调请求传递和解耦发送者和接收者的重要作用。每个具体处理者都实现了这个接口,以便在链中处理请求,从而形成了一种灵活的请求处理机制。这种模式适用于需要动态决定请求的处理方式,并且需要将请求在一系列对象之间传递的场景。
🦋1.2 具体处理者(Concrete Handler)
职责链模式(Chain of Responsibility Pattern)中的具体处理者(Concrete Handler)是实际处理请求的对象,它扮演了以下关键角色和作用:
实现处理接口:具体处理者必须实现抽象处理者定义的处理请求的接口或方法。这个方法通常是
handleRequest
或类似的名称,用于具体的请求处理逻辑。判断是否能处理请求:在处理请求之前,具体处理者通常会检查请求是否属于自己可以处理的类型。如果可以处理,则执行相应的处理逻辑;如果不能处理,则将请求传递给下一个处理者。
处理请求:如果具体处理者确定可以处理请求,它会执行与请求相关的具体业务逻辑,然后返回结果。这个处理逻辑可以包括修改请求的状态、生成响应、更新数据等操作。
传递请求:如果具体处理者不能处理请求,它会将请求传递给下一个处理者。通常,具体处理者会调用下一个处理者的处理方法,将请求传递给下一个环节。
维护后继者:具体处理者通常会持有对下一个处理者的引用,这个引用用于在无法处理请求时将请求传递给下一个处理者。
决定链的终止:具体处理者可以决定请求是否在当前处理者处终止,即不再传递给下一个处理者。这通常基于业务逻辑和处理者的责任范围。
责任的分配:每个具体处理者负责处理一种或多种请求类型,它们之间的责任是分散的,从而实现了责任的分配和解耦。
具体处理者在职责链模式中扮演了实际处理请求的角色,它们根据自己的能力和责任来决定是否处理请求,以及如何处理。具体处理
🦋1.3 请求类(Request)
职责链模式(Chain of Responsibility Pattern)中的请求类(Request)用于封装请求的信息和数据,它扮演了以下关键角色和作用:
封装请求数据:请求类负责封装请求所携带的数据、参数或信息,这些数据通常是需要被一系列处理者处理的。
定义请求类型:请求类通常包括一个字段或属性,用于标识请求的类型或种类。这有助于不同的具体处理者识别并决定是否能够处理该类型的请求。
传递请求:请求类的实例通过职责链模式中的处理者链传递,每个具体处理者可以检查请求的类型并根据需要处理请求。
扩展性:请求类的存在使得系统更容易扩展。新的请求类型可以通过创建新的请求类来实现,而无需修改已有的代码。这有助于保持系统的灵活性和可维护性。
请求上下文:请求类可以包含请求的上下文信息,例如请求的发起者、时间戳、附加数据等,以便处理者可以根据上下文做出决策。
可定制性:请求类可以根据具体需求进行定制,可以添加各种属性和方法,以满足不同场景下的请求处理需求。
请求类在职责链模式中用于封装和传递请求的关键信息。它有助于请求的处理者识别请求类型、处理请求,同时也提供了一种松耦合的方式来组织和扩展请求处理逻辑。通过合理设计请求类,可以使职责链模式更具可维护性和可扩展性。
🦋1.4 中间链条类(Chain)
职责链模式(Chain of Responsibility Pattern)中的中间链条类(Chain)通常是一个可选的组件,用于协调和管理具体处理者(Concrete Handler)组成的处理链。虽然中间链条类并不是职责链模式的必需部分,但它可以增强模式的灵活性和可配置性,其主要概念和作用如下:
协调请求处理流程:中间链条类负责协调和组织具体处理者的顺序,并在需要时将请求传递给下一个处理者。它充当了请求的入口点,负责启动整个处理链。
管理处理者的注册:中间链条类通常具有注册机制,允许动态添加、删除或配置具体处理者。这使得系统可以在不修改代码的情况下灵活地调整处理链。
提供默认处理逻辑:中间链条类可以提供一个默认的处理逻辑,以处理那些没有明确处理者的请求。这样可以确保每个请求都得到处理,即使没有合适的具体处理者。
简化客户端代码:通过中间链条类,客户端代码可以更加简洁,只需要将请求发送给中间链条类,而不需要了解具体处理者的细节。这有助于降低客户端与处理者之间的耦合度。
支持动态配置和组装:中间链条类使得系统可以根据需要动态配置和组装处理链,从而适应不同的请求处理需求。
实现自定义策略:中间链条类还可以根据具体场景实现自定义的策略,例如根据请求的类型或优先级来确定处理者的调用顺序。
中间链条类在职责链模式中扮演了协调和管理处理者链的角色,它增强了模式的灵活性和可配置性,使得系统能够更容易地适应不同的请求处理需求。虽然不是必需的,但在需要动态配置和管理处理链时,中间链条类可以提供重要的支持。
🔎2.示例
命名空间ChainOfResponsibility包含领导Leader类充当处理者基类,它包含4个实现类,经理类Manager、总监类Inspector、总经理类President和董事Directorate类,请假信息LeaveRequest类充当请求类,LeaderChain类充当中间链条类。本案例尝试以员工请假来解释职责链模式在审批环节的应用。
public class LeaveRequest {
public int Days { get; set; }
public string Name { get; set; }
public LeaveRequest(int days, string name) {
Days = days;
Name = name;
}
}
请假请求LeaveRequest类,包含需要请假的天数和员工的姓名。一个公开的构造函数表明调用方必须提供请假天数和员工姓名信息。
public abstract class Leader {
protected string Name { get; set; }
protected Leader(string name) {
this.Name = name;
}
public static LeaderChain Chain { protected get; set; }
public abstract void ProcessRequest(LeaveRequest request);
protected void Delivery(LeaveRequest request) {
Chain.DoChain(request);
}
}
领导者Leader类,充当处理者基类,包含领导的姓名Name并维持对中间链的引用。ProcessRequest为处理请假的抽象方法,为处理请假公开了一个调用接口。Delivery则为在不能处理请求时转派至下一个处理者。
public class Manager : Leader {
public Manager(string name) : base(name) { }
public override void ProcessRequest(LeaveRequest request) {
if (request.Days <= 2) {
Console.WriteLine($"{this.Name} approved {request.Name}'s " +
$"leave request for {request.Days} days!");
return;
}
Delivery(request);
}
}
具体处理者,经理Manager类,如果员工的请假天数小于等于2天,则经理有权限批准该请假请求。
public class Inspector : Leader {
public Inspector(string name) : base(name) { }
public override void ProcessRequest(LeaveRequest request) {
if (request.Days <= 4) {
Console.WriteLine($"{this.Name} approved {request.Name}'s " +
$"leave request for {request.Days} days!");
return;
}
Delivery(request);
}
}
具体处理者,总监Inspector类,如果员工的请假天数小于等于4天,则总监有权限批准该请假请求。
public class President : Leader {
public President(string name) : base(name) { }
public override void ProcessRequest(LeaveRequest request) {
if (request.Days <= 8) {
Console.WriteLine($"{this.Name} approved {request.Name}'s " +
$"leave request for {request.Days} days!");
return;
}
Delivery(request);
}
}
具体处理者,总经理President类,如果员工的请假天数小于等于8天,则总经理有权限批准该请假请求。
public class Directorate : Leader {
public Directorate(string name) : base(name) { }
public override void ProcessRequest(LeaveRequest request) {
if (request.Days > 8) {
Console.WriteLine($"{this.Name} approved {request.Name}'s " +
$"leave request for {request.Days} days!");
return;
}
Delivery(request);
}
}
具体处理者,董事Directorate类,如果员工的请假天数大于8天,则需要董事会批准该请假请求。
public class LeaderChain {
private List<Leader> _leaders = new List<Leader>();
private int _cursor = 0;
public void Attach(Leader leader) {
if (leader == null) throw new ArgumentNullException();
_leaders.Add(leader);
}
public bool Detach(Leader leader) {
if (leader == null) throw new ArgumentNullException();
return _leaders.Remove(leader);
}
public void DoChain(LeaveRequest request) {
if (_cursor <= _leaders.Count - 2) {
_leaders[++_cursor].ProcessRequest(request);
}
_cursor = 0;
}
}
中间链条类LeaderChain,首先内部维持对所有处理者的引用,包含的游标_cursor指示链条所处的位置,Attach和Detach方法分别向链条中增加和删除处理者。而DoChain方法则真正转派请求至下一个处理者。
此处需要注意的是,由于请求信息是由第一个处理者直接调用的,所以初始游标位置为0并且在DoChain方法中使用++_cursor作为处理者列表的索引参数。也就是说当第一次转派请求时,索引的值为1(因为使用了++_cursor),即为链条中的第2个处理者。请各位看官仔细思考此处逻辑。
public class Program {
public static void Main(string[] args) {
var leaders = new List<Leader>{
new Manager("Tom"),
new Inspector("Juice"),
new President("Iori"),
new Directorate("Marin")
};
var chain = new LeaderChain();
foreach (var leader in leaders) {
chain.Attach(leader);
}
Leader.Chain = chain;
var requests = new List<LeaveRequest> {
new LeaveRequest(1, "Zhao"),
new LeaveRequest(3, "Qian"),
new LeaveRequest(5, "Sun"),
new LeaveRequest(7, "Li"),
new LeaveRequest(12, "Zhou")
};
foreach (var request in requests) {
leaders[0].ProcessRequest(request);
}
Console.ReadKey();
}
}
以上为调用方代码的示例,首初始化一个处理者列表并增加至中间链条类,之后模拟“赵、钱、孙、李、周”5位同学的请假请求,他们分别要请1、3、5、7、12天假,最后调用ProcessRequest处理请求。以下是这个案例的输出结果:
Tom approved Zhao's leave request for 1 days!
Juice approved Qian's leave request for 3 days!
Iori approved Sun's leave request for 5 days!
Iori approved Li's leave request for 7 days!
Marin approved Zhou's leave request for 12 days!
🚀总结
🔎1.优点
职责链模式(Chain of Responsibility Pattern)具有多个优点,使其成为一种有用的设计模式,以下是一些主要的优点:
责任分离:职责链模式将请求的发送者与接收者解耦,每个具体处理者只关心自己能够处理的请求类型,从而实现了责任的分离。
可扩展性:新的具体处理者可以很容易地添加到链中,而不需要修改已有的代码。这使得系统具有高度的可扩展性,可以灵活地适应变化的需求。
动态配置处理链:职责链模式允许在运行时动态配置和组装处理链,可以根据不同的情况选择不同的处理者链,从而实现了灵活性和可配置性。
降低耦合度:客户端与具体处理者之间的耦合度降低,客户端只需要知道如何发送请求,而不需要了解具体处理者的细节,这有助于维护和扩展系统。
符合单一职责原则:每个具体处理者只负责处理一种或少数几种请求类型,符合单一职责原则,使得代码更加清晰和易于理解。
自动传递请求:职责链模式自动传递请求给下一个处理者,无需手动干预,确保请求得到适当的处理。
处理者重用:具体处理者可以在不同的处理链中重复使用,避免了代码的重复编写,提高了代码的重用性。
增强系统灵活性:职责链模式增强了系统的灵活性,可以根据不同的场景和需求定制不同的处理链,而无需修改核心代码。
职责链模式有助于构建松耦合、可扩展和灵活的系统,使得请求的处理变得更加可配置和可定制,同时降低了维护成本,提高了代码的可维护性和可扩展性。这些优点使得职责链模式在处理复杂请求处理逻
🔎2.缺点
虽然职责链模式(Chain of Responsibility Pattern)具有许多优点,但也存在一些缺点,需要考虑和权衡:
请求无法保证被处理:如果没有设置适当的具体处理者来处理请求,请求可能会被无视而无法得到处理。这可能导致请求丢失或未经处理,需要谨慎配置处理链。
性能问题:当处理链较长时,请求需要依次经过每个具体处理者,可能导致性能下降。特别是如果处理链较长且没有合适的处理者可以处理请求,请求将遍历整个链条,浪费时间。
复杂性增加:在复杂的职责链中,可能会变得难以理解和维护。维护一条庞大的职责链可能会导致代码的复杂性增加,降低代码的可读性。
难以调试:当请求在多个处理者之间传递时,调试可能会变得更加复杂。难以确定请求的流转路径以及在哪个具体处理者出现问题。
过度使用:职责链模式不适用于所有场景,过度使用它可能会导致设计变得过于复杂。在简单的情况下,使用职责链模式可能会显得繁琐和不必要。
不一定适合有序处理:职责链模式适合按照一定的顺序处理请求,但不适合需要并行处理或跳过某些处理者的情况。
可能导致循环引用:如果在处理链的配置中出现循环引用,可能会导致无限循环或堆栈溢出等问题,需要小心避免。
职责链模式适用于某些特定的场景,但不适合所有情况。在使用时,需要仔细考虑处理链的配置、性能问题以及代码的可维护性,以确保它能够达到预期的效果。
🔎3.使用场景
职责链模式(Chain of Responsibility Pattern)适用于以下一些场景和情况:
多个对象可以处理同一请求:当一个请求可以被多个不同类型的处理者处理,但实际处理者在运行时确定时,职责链模式是一个合适的选择。例如,一个订单处理系统中,订单可以被普通员工、经理和高级管理层处理,但最终的处理者需要在运行时确定。
请求的处理顺序不确定或可配置:如果请求的处理顺序在系统运行时可能会发生变化,或者需要根据条件进行动态配置,职责链模式是一种灵活的解决方案。例如,根据不同类型的客户请求,可以动态调整客户服务的处理顺序。
避免发送者与接收者的耦合:职责链模式可以降低发送者与接收者之间的耦合度,使得发送者无需知道请求的确切处理者是谁。这有助于保持系统的灵活性和可维护性。
需要动态添加或移除处理者:如果系统需要支持动态添加或移除处理者,职责链模式可以很容易地实现这一需求。新的处理者可以随时加入处理链,而不会影响已有代码。
每个处理者处理一个特定责任:职责链模式适用于每个具体处理者都负责处理特定类型或特定方面的请求。这有助于实现单一职责原则,并使代码更加模块化和可维护。
请求处理可能需要多个步骤:如果请求的处理涉及多个步骤,每个步骤由不同的处理者负责,职责链模式可以用来将这些步骤组织起来,实现请求的逐步处理。
处理者之间存在优先级:如果不同的处理者具有不同的优先级,可以按照优先级的顺序配置处理链,以确保请求被适当的处理者处理。
职责链模式在需要将请求发送给多个潜在处理者,并且能够动态配置和扩展处理链的情况下非常有用。它有助于实现松耦合的系统,提高了系统的灵活性和可维护性,同时符合单一职责原则。
🚀感谢:给读者的一封信
亲爱的读者,
我在这篇文章中投入了大量的心血和时间,希望为您提供有价值的内容。这篇文章包含了深入的研究和个人经验,我相信这些信息对您非常有帮助。
如果您觉得这篇文章对您有所帮助,我诚恳地请求您考虑赞赏1元钱的支持。这个金额不会对您的财务状况造成负担,但它会对我继续创作高质量的内容产生积极的影响。
我之所以写这篇文章,是因为我热爱分享有用的知识和见解。您的支持将帮助我继续这个使命,也鼓励我花更多的时间和精力创作更多有价值的内容。
如果您愿意支持我的创作,请扫描下面二维码,您的支持将不胜感激。同时,如果您有任何反馈或建议,也欢迎与我分享。
再次感谢您的阅读和支持!
最诚挚的问候, “愚公搬代码”
- 点赞
- 收藏
- 关注作者
评论(0)