惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Webroot Blog
Webroot Blog
U
Unit 42
A
About on SuperTechFans
宝玉的分享
宝玉的分享
月光博客
月光博客
C
CERT Recently Published Vulnerability Notes
P
Privacy International News Feed
Microsoft Security Blog
Microsoft Security Blog
G
Google Developers Blog
P
Privacy & Cybersecurity Law Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
S
Securelist
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Spread Privacy
Spread Privacy
L
Lohrmann on Cybersecurity
Apple Machine Learning Research
Apple Machine Learning Research
K
Kaspersky official blog
Hugging Face - Blog
Hugging Face - Blog
B
Blog
I
Intezer
Last Week in AI
Last Week in AI
T
Threat Research - Cisco Blogs
V
V2EX
L
LangChain Blog
AI
AI
G
GRAHAM CLULEY
T
Tor Project blog
人人都是产品经理
人人都是产品经理
D
Docker
WordPress大学
WordPress大学
Google DeepMind News
Google DeepMind News
I
InfoQ
Y
Y Combinator Blog
C
Comments on: Blog
GbyAI
GbyAI
www.infosecurity-magazine.com
www.infosecurity-magazine.com
酷 壳 – CoolShell
酷 壳 – CoolShell
T
Tailwind CSS Blog
aimingoo的专栏
aimingoo的专栏
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
腾讯CDC
N
News and Events Feed by Topic
MyScale Blog
MyScale Blog
H
Help Net Security
Vercel News
Vercel News
T
Tenable Blog
博客园 - 三生石上(FineUI控件)
爱范儿
爱范儿

博客园 - 网络金领

C# WinForm 导出导入Excel/Doc 完整实例教程[使用Aspose.Cells.dll] DataTable的数据批量写入数据库 C# .NET锁屏程序(顺带屏蔽任务管理器) 步步为营 .NET 代码重构学习 十一 - 网络金领 步步为营 .NET 代码重构学习笔记 十 步步为营 .NET 代码重构学习笔记 九 步步为营 .NET 代码重构学习笔记 八 步步为营 .NET 代码重构学习笔记 七 步步为营 .NET 代码重构学习笔记 六、移动函数和移动值域(Move Method And Move Field) 步步为营 .NET 代码重构学习笔记 五、分解函数和替换算法(Replace Method And Substitute Algorithm) 步步为营 .NET 代码重构学习笔记 四、临时变量(Temporary Variable) 步步为营 .NET 代码重构学习笔记 二、提炼方法(Extract Method) 步步为营 .NET 代码重构学习笔记 三、内联方法(Inline Method) 步步为营 .NET 代码重构学习笔记 一、为何要代码重构 步步为营 .NET 设计模式学习笔记 二十四、Factory Method(工厂方法模式) 步步为营 .NET 设计模式学习笔记 二十三、Interpreter(解释器模式) 步步为营 .NET 设计模式学习笔记 二十二、Memento(备望录模式) 步步为营 .NET 设计模式学习笔记 二十一、Visitor(访问者模式) 步步为营 .NET 设计模式学习笔记 二十、Mediator(中介者模式)
步步为营 .NET 设计模式学习笔记 十九、Chain of Responsibility(职责链模式)
网络金领 · 2011-06-01 · via 博客园 - 网络金领

概述

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

意图

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

结构图

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

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

生活中的例子

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

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

示例用例图

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

image

代码设计

先创建BorrowApplication.cs:

01 public class BorrowApplication
03     private string _Name;
04     private double _Money;
05     private string _Purpose;
07     public string Name
09         get { return _Name; }
10         set { _Name = value; }
13     public double Money
15         get { return _Money; }
16         set { _Money = value; }
19     public string Purpose
21         get { return _Purpose; }
22         set { _Purpose = value; }
25     public BorrowApplication(string name, double money, string purpose)
27         this._Name = name;
28         this._Money = money;
29         this._Purpose = purpose;

再创建Approve.cs:

01 public abstract class Approve
03     private string _Name;
04     private Approve _HighApprove;
06     public string Name
08         get { return _Name; }
09         set { _Name = value; }
12     public Approve HighApprove
14         get { return _HighApprove; }
15         set { _HighApprove = value; }
18     public Approve(string name)
20         this._Name = name;
26     /// <param name="approve"></param>
27     public void SetHighApprove(Approve approve)
29         _HighApprove = approve;
35     /// <param name="borrowApplication"></param>
36     /// <returns></returns>
37     public abstract string HandleApplication(BorrowApplication borrowApplication);
42     /// <param name="money"></param>
43     /// <param name="borrowApplication"></param>
44     /// <returns></returns>
45     public string ApproveLimit(double money, BorrowApplication borrowApplication)
47         string result = string.Empty;
48         if (borrowApplication.Money < money)
50             result = string.Format("{0} 同意{1}因为{2}产生的借款{3}.\n",  Name, borrowApplication.Name, borrowApplication.Purpose, borrowApplication.Money);
54             result = string.Format("{0}回复申请,{1}因为{2}产生的借款{3}需要{4}审核.\n", Name, borrowApplication.Name, borrowApplication.Purpose, borrowApplication.Money, HighApprove.Name);
55             result += HighApprove.HandleApplication(borrowApplication);
57         return result;

再创建Manager.cs:

01 public class Manager : Approve
03     public override string HandleApplication(BorrowApplication borrowApplication)
05         return ApproveLimit(5000, borrowApplication);
08     public Manager(string name)

再创建Director.cs:

01 public class Director : Approve
03     public override string HandleApplication(BorrowApplication borrowApplication)
05         return ApproveLimit(20000, borrowApplication);
09     public Director(string name)

再创建Chairman.cs:

01 public class Chairman : Approve
03     public override string HandleApplication(BorrowApplication borrowApplication)
05         return ApproveLimit(int.MaxValue, borrowApplication);
08     public Chairman(string name)

最后调用

01 public partial class Run : Form
05         InitializeComponent();
08     private void btnRun_Click(object sender, EventArgs e)
11         //-------------------------------------
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);
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, "美国全球峰会")));

结果如下图:

image

实现要点

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

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

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

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

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

适用性

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

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

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

总结

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