-
定义
避免请求发送者与接受者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
UML类图如下:
其中类和对象的关系:
1. Handler(传递者接口):定义一个处理请求的接口;实现链中下一个对象(可选)。
2. ConcreteHandler(具体传递者):处理它所负责的请求;可以访问链中下一个对象;如果可以处理请求,就处理它,否则将请求转发给后续者。
3. Client(客户应用程序):向链中的对象提出最初的请求。
典型应用的顺序图如下:
-
实例1——采购分级审批
一般的企业采购审批都是分级的,采购量的不同就需要不同层次的主管人员来审批,如主任可以审批1万元以下的采购单,副董事长可以审批2.5万元以下的采购单,董事长可以审批10万元以下的采购单,10万元以上的就要开会判定。UML类图如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
abstract class Approver
{
protected string name;
protected Approver successor; // 上级
public Approver( string name)
{
this .name = name;
}
public void SetSuccessor(Approver successor)
{
this .successor = successor;
}
abstract public void ProcessRequest(PurchaseRequest request);
}
// 具体传递者—主任
class Director : Approver
{
public Director( string name) : base (name) { }
public override void ProcessRequest(PurchaseRequest request)
{
// 总额小于10000就处理
if (request.Amount < 10000.0 )
{
Console.WriteLine( " {0} {1} 审批了采购请求#{2} " , " 主任 " , name, request.Number);
}
else if (successor != null )
{
successor.ProcessRequest(request);
}
}
}
// 具体传递者—副董事长
class VicePresident : Approver
{
public VicePresident( string name) : base (name) { }
public override void ProcessRequest(PurchaseRequest request)
{
// 总额小于25000就处理
if (request.Amount < 25000 )
{
Console.WriteLine( " {0} {1} 审批了采购请求#{2} " , " 副董事长 " , name, request.Number);
}
else if (successor != null )
{
successor.ProcessRequest(request);
}
}
}
// 具体传递者—董事长
class President : Approver
{
public President( string name) : base (name) { }
public override void ProcessRequest(PurchaseRequest request)
{
// 总额小于100000就处理
if (request.Amount < 100000 )
{
Console.WriteLine( " {0} {1} 审批了采购请求#{2} " , " 董事长 " , name, request.Number);
}
else
{
Console.WriteLine( " 采购请求#{0}需要开会来决定! " , request.Number);
}
}
}
// 采购单
class PurchaseRequest
{
private int number;
private double amount;
private string purpose;
public PurchaseRequest( int number, double amount, string purpose)
{
this .number = number;
this .amount = amount;
this .purpose = purpose;
}
public int Number
{
get { return number; }
set { number = value; }
}
public double Amount
{
get { return amount; }
set { amount = value; }
}
public string Purpose
{
get { return purpose; }
set { purpose = value; }
}
}
// 客户应用测试
class Client
{
[STAThread]
static void Main( string [] args)
{
Director Larry = new Director( " Larry " );
VicePresident Sam = new VicePresident( " Sam " );
President Tammy = new President( " Tammy " );
Larry.SetSuccessor(Sam);
Sam.SetSuccessor(Tammy);
PurchaseRequest rs = new PurchaseRequest( 2034 , 350 , " Supplies " );
Larry.ProcessRequest(rs);
PurchaseRequest rx = new PurchaseRequest( 2035 , 35000 , " Proejct X " );
Larry.ProcessRequest(rx);
PurchaseRequest ry = new PurchaseRequest( 2036 , 350000 , " Proejct Y " );
Larry.ProcessRequest(ry);
Console.Read();
}
}
-
实例2——智能大厦安全系统
假定我们要做一个智能大厦安全系统。安全系统包括很多各种传感器(运动检测器、烟感检测器、温度检测器),它们的状态会传给计算机。计算机系统的工作是记录这些状态并在紧急事件发生事发出警报。我们设计的系统要有高度可扩展性,它可以适用于小型零售店、办公楼、仓库、多功能大厦等多种场合。
我们的监控程序会为每一个传感器实例生成一个对象,而所在的安全区域会分为区域(Area)、仓库(Warehouse)和大楼(Building)。我们假定安全系统中仓库的办公室一旦温度超过150摄制度就要打开喷洒器喷水,然后系统会通知仓库保安去现场检查,系统在仓库所在大楼发出火警。
其UML类图如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
public abstract class Sensor
{
// 所安装的位置
public string position;
}
// 温度传感器
public class TemperatureSensor : Sensor
{
public TemperatureSensor( string position)
{
this .position = position;
}
}
// 定义安全区抽象类
public abstract class SecurityZone
{
// 父区域
private SecurityZone parent;
// 区域名
public string name;
public SecurityZone( string name)
{
this .name = name;
}
// 返回它的父区域
public SecurityZone GetParent()
{
return parent;
}
// 设定它的负区域
public void SetParent(SecurityZone zone)
{
this .parent = zone;
}
// 调用这个方法去通知区域对象其中的传感器的测量值
public void Notify( int measurement, Sensor sensor)
{
if ( ! HandleNotification(measurement, sensor) && parent != null )
parent.Notify(measurement, sensor);
}
// 上面的方法notify调用这个方法让对象可以处理测量值
public abstract bool HandleNotification( int measurement, Sensor sensor);
// 本方法被子区域调用来报告火警,期待子区域中打开喷洒器
// 重载此方法可以让父区域也采取必要的行动
public virtual void FireAlarm(SecurityZone zone, Sensor sensor)
{
Console.WriteLine( this .name + sensor.position + " 水喷洒器打开了 " );
if (parent != null )
{
parent.FireAlarm(zone, sensor);
}
}
}
// 具体区域,继承SecurityZone类
public class Area : SecurityZone
{
public Area( string name) : base (name) { }
public override bool HandleNotification( int measurement, Sensor sensor)
{
if (measurement > 150 )
{
FireAlarm( this , sensor);
return true ;
}
return false ;
}
}
// 具体区域,仓库类,有火时,呼叫保安马上检查发货区域
public class Warehouse : SecurityZone
{
public Warehouse( string name) : base (name) { }
// 仓库下的区域已经处理传感器的测量值,这里不作处理
public override bool HandleNotification( int measurement, Sensor sensor)
{
return false ;
}
public override void FireAlarm(SecurityZone zone, Sensor sensor)
{
if (zone.GetType() == typeof (Area))
{
Console.WriteLine( " 保安请马上去检查{0}的{1} " , this .name, zone.name);
if (GetParent() != null )
GetParent().FireAlarm(zone, sensor);
return ;
}
base .FireAlarm(zone, sensor);
}
}
// 具体区域,大楼类,采取行动,通报火警
public class Building : SecurityZone
{
public Building( string name) : base (name) { }
public override bool HandleNotification( int measurement, Sensor sensor)
{
return false ;
}
public override void FireAlarm(SecurityZone zone, Sensor sensor)
{
if (zone.GetType() == typeof (Area))
{
Console.WriteLine( " {0}栋大楼发生火警 " , this .name);
if (GetParent() != null )
GetParent().FireAlarm(zone, sensor);
return ;
}
base .FireAlarm(zone, sensor);
}
}
// 客户应用测试
class Client
{
[STAThread]
static void Main( string [] args)
{
Area a = new Area( " 经理室 " );
Warehouse w = new Warehouse( " 仓库B " );
TemperatureSensor ts_at_corner = new TemperatureSensor( " 右边角 " );
a.SetParent(w); // 区域a在仓库w中
Building b = new Building( " #2 " );
w.SetParent(b); // 仓库w在大楼b中
a.Notify( 200 , ts_at_corner);
Console.Read();
}
}
-
优势和缺陷
责任链模式可以减少对象的连接,为对象责任分配增加了很大的灵活性。该模式允许把一组类作为一个类来使用,并且在类的组合中,一个类的事件可以发送到另一个类并由其处理。
责任链模式通常应用与图形用户界面中,窗体的部件可能会包含其他几个小部件,就如同Windows窗体应用程序中,控件中又可以放置其他控件,控件边界会决定是否处理事件,或者将事件传递给父控件来处理。
另外,责任链还会以树状出现,这样,一个事件可以传给多个类,或者,多个类的信息可以提交到一个类。树状责任链能够提供更灵活的技巧,但缺点是信息在树中容易迷失。
-
应用情景
下面的情景很适合应用责任链模式:
1. 超过一个对象能够处理客户请求并且到底哪个对象处理预先不知道。
2. 一个请求可以发布到多个对象但它的接收都是不清晰。
3. 可以动态指定一组对象处理请求。