十六.行为型设计模式——Chain of Responsibility(责任链模式)
-
定义
避免请求发送者与接受者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
UML类图如下:
其中类和对象的关系:
1. Handler(传递者接口):定义一个处理请求的接口;实现链中下一个对象(可选)。
2. ConcreteHandler(具体传递者):处理它所负责的请求;可以访问链中下一个对象;如果可以处理请求,就处理它,否则将请求转发给后续者。
3. Client(客户应用程序):向链中的对象提出最初的请求。
典型应用的顺序图如下:
-
实例1——采购分级审批
一般的企业采购审批都是分级的,采购量的不同就需要不同层次的主管人员来审批,如主任可以审批1万元以下的采购单,副董事长可以审批2.5万元以下的采购单,董事长可以审批10万元以下的采购单,10万元以上的就要开会判定。UML类图如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.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://images.cnblogs.com/OutliningIndicators/ContractedBlock.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. 可以动态指定一组对象处理请求。