• 推荐
  • 评论
  • 收藏

步步为营 .NET 设计模式学习笔记 十九、Chain of Responsibility(职责链模式)

2022-11-25    4285次浏览

概述

在软件构建过程中,一个请求可能被多个对象处理,但是每个请求在运行时只能有一个接受者,如果显示指定,将必不可少地带来请求发送者与接受者的紧耦合。
如何使请求的发送者不需要指定具体的接受者?让请求的接受者自己在运行时决定来处理请求,从而使两者解耦。

意图

责任链模式是一种对象的行为模式【GOF95】。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织链和分配责任。

结构图

抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义出一个方法,以设定和返回对下家的引用。这个角色通常由一个抽象类或接口实现。

具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。

生活中的例子

击鼓传花是一种热闹而又紧张的饮酒游戏。在酒宴上宾客依次坐定位置,由一人击鼓,击鼓的地方与传花的地方是分开的,以示公正。开始击鼓时,花束就开始依次传递,鼓声一落,如果花束在某人手中,则该人就得饮酒。

击鼓传花便是责任链模式的应用。责任链可能是一条直线、一个环链或者一个树结构的一部分。

示例用例图

在公司,员工都有出差的机会,出差就会产生借款,借款时就会产生这个申请单由谁来审批的问题,部门经理、总监和董事长各有不同的审批权限,看我们由这个需求设计的一个职责链模式,用例图如下:

image

代码设计

先创建BorrowApplication.cs:

01 public class BorrowApplication
02 {
03     private string _Name;
04     private double _Money;
05     private string _Purpose;
06  
07     public string Name
08     {
09         get { return _Name; }
10         set { _Name = value; }
11     }
12  
13     public double Money
14     {
15         get { return _Money; }
16         set { _Money = value; }
17     }
18  
19     public string Purpose
20     {
21         get { return _Purpose; }
22         set { _Purpose = value; }
23     }
24  
25     public BorrowApplication(string name, double money, string purpose)
26     {
27         this._Name = name;
28         this._Money = money;
29         this._Purpose = purpose;
30     }
31 }

再创建Approve.cs:

01 public abstract class Approve
02 {
03     private string _Name;
04     private Approve _HighApprove;
05  
06     public string Name
07     {
08         get { return _Name; }
09         set { _Name = value; }
10     }
11  
12     public Approve HighApprove
13     {
14         get { return _HighApprove; }
15         set { _HighApprove = value; }
16     }
17  
18     public Approve(string name)
19     {
20         this._Name = name;
21     }
22  
23     /// <summary>
24     /// 设置高一级审批对象
25     /// </summary>
26     /// <param name="approve"></param>
27     public void SetHighApprove(Approve approve)
28     {
29         _HighApprove = approve;
30     }
31  
32     /// <summary>
33     /// 处理申请
34     /// </summary>
35     /// <param name="borrowApplication"></param>
36     /// <returns></returns>
37     public abstract string HandleApplication(BorrowApplication borrowApplication);
38  
39     /// <summary>
40     /// 审批权限
41     /// </summary>
42     /// <param name="money"></param>
43     /// <param name="borrowApplication"></param>
44     /// <returns></returns>
45     public string ApproveLimit(double money, BorrowApplication borrowApplication)
46     {
47         string result = string.Empty;
48         if (borrowApplication.Money < money)
49         {
50             result = string.Format("{0} 同意{1}因为{2}产生的借款{3}.\n",  Name, borrowApplication.Name, borrowApplication.Purpose, borrowApplication.Money);
51         }
52         else
53         {
54             result = string.Format("{0}回复申请,{1}因为{2}产生的借款{3}需要{4}审核.\n", Name, borrowApplication.Name, borrowApplication.Purpose, borrowApplication.Money, HighApprove.Name);
55             result += HighApprove.HandleApplication(borrowApplication);
56         }
57         return result;
58     }
59 }

再创建Manager.cs:

01 public class Manager : Approve
02 {
03     public override string HandleApplication(BorrowApplication borrowApplication)
04     {
05         return ApproveLimit(5000, borrowApplication);
06     }
07  
08     public Manager(string name)
09         : base(name)
10     {
11     }
12 }

再创建Director.cs:

01 public class Director : Approve
02 {
03     public override string HandleApplication(BorrowApplication borrowApplication)
04     {
05         return ApproveLimit(20000, borrowApplication);
06     }
07  
08  
09     public Director(string name)
10         : base(name)
11     { }
12 }

再创建Chairman.cs:

01 public class Chairman : Approve
02 {
03     public override string HandleApplication(BorrowApplication borrowApplication)
04     {
05         return ApproveLimit(int.MaxValue, borrowApplication);
06     }
07  
08     public Chairman(string name)
09         : base(name)
10     { }
11 }

最后调用

01 public partial class Run : Form
02 {
03     public Run()
04     {
05         InitializeComponent();
06     }
07  
08     private void btnRun_Click(object sender, EventArgs e)
09     {
10  
11         //-------------------------------------
12  
13         Approve zhangshang = new Manager("zhangshang");
14         Approve lishi = new Director("lishi");
15         Approve wangwu = new Chairman("wangwu");
16         zhangshang.SetHighApprove(lishi);
17         lishi.SetHighApprove(wangwu);
18  
19         rtbResult.AppendText(zhangshang.HandleApplication(new BorrowApplication("xiaoli", 1000, "上海培训")));
20         rtbResult.AppendText(zhangshang.HandleApplication(new BorrowApplication("xiaoye", 10000, "北京技术年会")));
21         rtbResult.AppendText(zhangshang.HandleApplication(new BorrowApplication("renyingying", 100000, "美国全球峰会")));
22     }
23  
24 }

结果如下图:

image

实现要点

1.Chain of Responsibility模式的应用场合在于“一个请求可能有多个接受者,但是最后真正的接受者只胡一个”,只有这时候请求发送者与接受者的耦合才胡可能出现“变化脆弱”的症状,职责链的目的就是将二者解耦,从而更好地应对变化。
2.应用了Chain of Responsibility模式后,对象的职责分派将更具灵活性。我们可以在运行时动态添加/修改请求的处理职责。
3.如果请求传递到职责链的未尾仍得不到处理,应该有一个合理的缺省机制。这也是每一个接受对象的责任,而不是发出请求的对象的责任。

4.有一个抽象责任角色,避免各责任类型之间发生耦合。

5.抽象责任角色中定义了后继责任角色,并对外提供一个方法供客户端配置。

6.各具体责任类型根据待处理对象的状态结合自己的责任范围来判断是否能处理对象,如果不能处理提交给上级责任人处理(也就是纯的责任模式,要么自己处理要么提交给别人)。当然,也可以在进行部分处理后再提交给上级处理(也就是不纯的责任链模式)。

7.需要在客户端链接各个责任对象,如果链接的不恰当,可能会导致部分对象不能被任何一个责任对象进行处理。

适用性

1.有多个对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
2.你想在不明确接收者的情况下,向多个对象中的一个提交一个请求。
3.可处理一个请求的对象集合应被动态指定。

4.从代码角度来说,如果一个逻辑的处理由不同责任对象完成,客户端希望能自定义这个处理流程并且不希望直接和多个责任对象发生耦合的时候可以考虑责任链模式。

5.从应用角度来说,如果对一个事情的处理存在一个流程,需要经历不同的责任点进行处理,并且这个流程比较复杂或只希望对外公开一个流程的入口点的话可以考虑责任链模式。其实,责任链模式还可以在构架的层次进行应用,比如.NET中的层次异常处理关系就可以看作是一种责任链模式。

总结

1.责任链模式和状态模式的区别在于,前者注重责任的传递,并且责任链由客户端进行配置,后者注重对象状态的转换,这个转换过程对客户端是透明的。

原文地址:https://www.cnblogs.com/Leo_wl/p/2034633.html