模式是一种对象的行为模式【GOF95】。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织链和分配责任。
职责链模式是一种对象的行为模式【GOF95】。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织链和分配责任。
引入职责链模式的目的仍然是为了解除耦合,从“封装变化”的角度出发,它封装了“职责行为的变化”,同时又利用链结构保证了消息的传递,此模式的优势在于它能够“智能”地根据条件在职责链中定位到合适的对象,并执行其职能。
职责链模式的UML图如下:
责任链模式涉及到的角色如下所示:
1、抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义出一个方法,以设定和返回对下家的引用。这个角色通常由一个抽象类或接口实现。
2、具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。
下面,我们还是用代码来说明。
程序如下图:
一、职责链模式的基本思路
1、抽象处理者(Handler)角色:
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyChainResponsibility
{
abstract class Handler
{
//定义一个设置下一个处理者的接口
protected Handler _successor;
public void SetSuccessor(Handler sucessor)
{
this._successor = sucessor;
}
//定义一个处理信息的接口
public abstract void HandleRequest(int request);
}
}
2、具体处理者(ConcreteHandler)角色
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyChainResponsibility
{
第一个处理者#region 第一个处理者
class ConcreteHandlerOne:Handler
{
public override void HandleRequest(int request)
{
if(request>=0 && request <10)
{
Console.WriteLine("{0} handled request{1}",this.GetType().Name,request);
}
else if (_successor != null)
{
_successor.HandleRequest(request);
}
}
}
#endregion
第二个处理者#region 第二个处理者
class ConcreteHandlerTwo : Handler
{
public override void HandleRequest(int request)
{
if (request >= 10 && request < 40)
{
Console.WriteLine("{0} handled request{1}", this.GetType().Name, request);
}
else if (_successor != null)
{
_successor.HandleRequest(request);
}
}
}
#endregion
第三个处理者#region 第三个处理者
class ConcreteHandlerThree : Handler
{
public override void HandleRequest(int request)
{
if (request >= 40 && request < 100)
{
Console.WriteLine("{0} handled request{1}", this.GetType().Name, request);
}
else if (_successor != null)
{
_successor.HandleRequest(request);
}
}
}
#endregion
}
3、客户端应用
Code
#region 基本思路示例
Console.WriteLine("-------基本思路示例----------");
Handler h1 = new ConcreteHandlerOne();
Handler h2 = new ConcreteHandlerTwo();
Handler h3 = new ConcreteHandlerThree();
//设置责任链
h1.SetSuccessor(h2);
h2.SetSuccessor(h3);
int[] requests = {2,5,21,22,18,26,89};
foreach (int request in requests)
{
//责任处理
h1.HandleRequest(request);
}
Console.ReadKey();
#endregion
二、关于办公室发票报销的职责链处理模式
办公室的发票,根据它们的金额大小分别由科长,处长,局长逐级负责审阅报批,职责链处理流程从科长到处长再到局长。代码如下
1、抽象处理者(Handler)角色:ApproveHandler.
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyChainResponsibility
{
abstract class ApproveHandler
{
#region 职务属性
private string headship;
public string HeadShip
{
get { return headship; }
set { headship = value; }
}
#endregion
#region 构造函数
public ApproveHandler(string HeadShip)
{
this.headship = HeadShip;
}
#endregion
#region 设置后继者接口
protected ApproveHandler _successor;
public void SetSuccessor(ApproveHandler successor)
{
this._successor = successor;
}
#endregion
#region 处理职责接口
public abstract void HandleRequest(Receipt request);
#endregion
}
}
2、具体处理者(ConcreteHandler)角色:ConcreteApproveHandlers
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyChainResponsibility
{
科长#region 科长
class KeZhang:ApproveHandler
{
public KeZhang(string duty):base(duty)
{
}
public override void HandleRequest(Receipt request)
{
if (request.Amount > 0 && request.Amount < 500)
{
Console.WriteLine("{0}负责审阅此帐单,帐单金额为: {1},帐单用途为: {2}",this.HeadShip,request.Amount,request.Purpose);
}
else if (_successor!=null)
{
_successor.HandleRequest(request);
}
}
}
#endregion
处长#region 处长
class ChuZhang : ApproveHandler
{
public ChuZhang(string duty)
: base(duty)
{
}
public override void HandleRequest(Receipt request)
{
if (request.Amount > 500 && request.Amount < 1000)
{
Console.WriteLine("{0}负责审阅此帐单,帐单金额为: {1},帐单用途为: {2}", this.HeadShip, request.Amount, request.Purpose);
}
else if (_successor != null)
{
_successor.HandleRequest(request);
}
}
}
#endregion
局长#region 局长
class JuZhang : ApproveHandler
{
public JuZhang(string duty)
: base(duty)
{
}
public override void HandleRequest(Receipt request)
{
if (request.Amount > 1000)
{
Console.WriteLine("{0}负责审阅此帐单,帐单金额为: {1},帐单用途为: {2}", this.HeadShip, request.Amount, request.Purpose);
}
else if (_successor != null)
{
_successor.HandleRequest(request);
}
}
}
#endregion
}
3、客户端应用
Receipt发票类定义
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyChainResponsibility
{
#region 发票类
class Receipt
{
private double _amount;
private string _purpose;
// 构造函数
public Receipt( double amount, string purpose)
{
this._amount = amount;
this._purpose = purpose;
}
#region 金额
public double Amount
{
get { return _amount; }
set { _amount = value; }
}
#endregion
#region 用途
public string Purpose
{
get { return _purpose; }
set { _purpose = value; }
}
#endregion
}
#endregion
}
客户端应用
Code
#region 办公室报销责任链
Console.WriteLine("-------办公室报销责任链----------");
KeZhang ke = new KeZhang("科长");
ChuZhang chu = new ChuZhang("处长");
JuZhang ju = new JuZhang("局长");
Receipt r1 = new Receipt(360, "请客吃饭.");
Receipt r2 = new Receipt(760, "处室活动开销.");
Receipt r3 = new Receipt(360, "采购办公用品.");
//设置责任链
ke.SetSuccessor(chu);
chu.SetSuccessor(ju);
//责任处理
ke.HandleRequest(r1);
ke.HandleRequest(r2);
ke.HandleRequest(r3);
Console.ReadKey();
#endregion
总结
1、何时采用
从代码角度来说,如果一个逻辑的处理由不同责任对象完成,客户端希望能自定义这个处理流程并且不希望直接和多个责任对象发生耦合的时候可以考虑责任链模式。
从应用角度来说,如果对一个事情的处理存在一个流程,需要经历不同的责任点进行处理,并且这个流程比较复杂或只希望对外公开一个流程的入口点的话可以考虑责任链模式。其实,责任链模式还可以在构架的层次进行应用,比如.NET中的层次异常处理关系就可以看作是一种责任链模式。
2、实现要点
(1)有一个抽象责任角色,避免各责任类型之间发生耦合。
(2)抽象责任角色中定义了后继责任角色,并对外提供一个方法供客户端配置。
(3)各具体责任类型根据待处理对象的状态结合自己的责任范围来判断是否能处理对象,如果不能处理提交给上级责任人处理(也就是纯的责任模式,要么自己处理要么提交给别人)。当然,也可以在进行部分处理后再提交给上级处理(也就是不纯的责任链模式)。
(4)需要在客户端链接各个责任对象,如果链接的不恰当,可能会导致部分对象不能被任何一个责任对象进行处理。
3、注意事项
责任链模式和状态模式的区别在于,前者注重责任的传递,并且责任链由客户端进行配置,后者注重对象状态的转换,这个转换过程对客户端是透明的。
前往:设计模式学习笔记清单