职责链(Chain of Responsibility)职责链模式是一种行为模式,当多个对象都有机会处理请求的时候,为了避免请求的发送者和接收者之间的耦合关系,我们使用职责链模式将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
职责链模式最常用的地方就是在UI组件响应点击事件的时候,今天我们就以此为例。
首先我们定义了一个基类:
public class Widget
{
protected Widget _parent;
public Widget(Widget parent)
{
_parent = parent;
}
public virtual bool CanResponse(int x, int y)
{
return false;
}
public virtual void HandleTouch(int x, int y)
{
if (CanResponse(x, y)) {
DoHandleTouch (x, y);
} else {
if (_parent != null) {
_parent.HandleTouch (x, y);
}
}
}
protected virtual void DoHandleTouch(int x, int y)
{
//TODO
Console.WriteLine("Touch on widget");
}
}
在这个Widget基类里面,就定义了职责链的一些基本操作:连接后继者(parent),是否响应事件(CanResponse),传递事件(HandleTouch),处理事件(DoHandleTouch)。
我们可以实现继承自Widget的两个类:
public class Button : Widget
{
protected int _x;
protected int _y;
protected int _w;
protected int _h;
public Button(Widget parent, int x, int y, int w, int h) : base(parent)
{
_x = x;
_y = y;
_w = w;
_h = h;
}
public override bool CanResponse(int x, int y)
{
return ContainPoint (x, y);
}
protected bool ContainPoint(int x, int y)
{
return x >= _x && x < _x + _w && y >= _y && y < _y + _h;
}
protected override void DoHandleTouch(int x, int y)
{
//TODO
Console.WriteLine("Touch on button");
}
}
public class Image : Widget
{
public Image (Widget parent) : base(parent)
{
}
public override bool CanResponse(int x, int y)
{
return !IsTransparentAtPoint (x, y);
}
protected bool IsTransparentAtPoint(int x, int y)
{
//TODO
return false;
}
protected override void DoHandleTouch(int x, int y)
{
//TODO
Console.WriteLine("Touch on image");
}
}
使用:
Widget wdt = new Widget (null);
Image img = new Image (wdt);
Button btn = new Button (img, 0, 0, 100, 100);
btn.HandleTouch (200, 300);
因为传入的x,y不在btn的区域里,所以将事件传递给了img,由img来处理事件。
职责链模式让我想起了RPG游戏里面两个很有趣的技能,护卫和伤害转移,简单实现以下:
public abstract class Battler
{
public string Name { get; protected set;}
public Battler(string name)
{
Name = name;
}
protected Battler _damageTaker;
public void TransferDamageTo(Battler taker)
{
_damageTaker = taker;
}
public int HP { get; set;}
public void BeDamaged(int dmg)
{
if (_damageTaker != null) {
_damageTaker.BeDamaged (dmg);
} else {
HP -= dmg;
Console.WriteLine (Name + " is damaged");
}
}
public int Attack{ get; set; }
public void AttackTo(Battler battler)
{
Console.WriteLine (Name + "is attacking");
battler.BeDamaged (Attack);
}
}
public class Hero : Battler
{
public Hero(string name) : base(name)
{
HP = 100;
Attack = 20;
}
public void Protect(Hero other)
{
other.TransferDamageTo (this);
}
}
public class Monster : Battler
{
public Monster(string name) : base(name)
{
HP = 200;
Attack = 10;
}
}
使用:
Hero snow = new Hero ("snow");
Hero pino = new Hero ("pino");
Monster monster = new Monster ("monster");
Monster frog = new Monster ("forg");
pino.Protect (snow);
pino.TransferDamageTo (monster);
frog.AttackTo (snow);
pino护卫了snow,又将伤害转移给frog,所以当monster攻击的时候,frog承受了伤害。
这段代码算是职责链模式的变种,因为职责链模式只有本对象无法处理事件的时候才会传递给后继者,而这段代码是只有没有后继者的时候才会处理事件。
职责链模式的优点在于降低了耦合度,事件的发送者和接收者都没有对方明确的信息,而且我们可以实现多种多样的职责链结构(例如树形),而链中的对象不需要知道它所处的是怎样的结构。并且职责链模式增强了分派事件的灵活性,可以动态的修改链的结构,增加、删除或者调整顺序。
缺点在于,不能保证事件可以被处理,因为并不一定有对象可以相应事件。而且这样也增加了系统的复杂性,会增加排错的难度。