行为型模式——责任链(Chain of Responsibility)
问题背景
当希望让多个对象都有处理请求的机会时,考虑使用责任链。责任链的应用场景比较有限,这里就直接照搬书中的案例了。考虑一个图形用户界面中的上下文有关的帮助机制:用户在不同的界面点击帮助按钮,会显示不同的帮助文档。如果界面有特定的帮助文档,则显示特定的帮助文档;如果界面没有特定的帮助文档,则显示上层界面的帮助文档;如果上层界面也没有,则继续向上……这种递归传递请求,直到有人处理的逻辑,就是责任链。
解决方案
对于所有控件,都应该有一个HandleHelp方法用来处理帮助请求,这个方法的逻辑是:检查自己是否能够处理请求,如果能,则直接处理;如果不能,则将请求转发给下一个对象(父控件)。所以,每个控件还应该维护一个对其父控件的引用。使用责任链的程序结构是这样的:
这样,在用户发出请求时,系统就会自动递归地传递请求,直到有人处理。
效果
- 使多个对象都有机会处理同一个请求。
- 使用户和具体对象解耦。
缺陷
责任链无法保证用户的请求一定被处理,因为请求可能传递到最后都没人能处理。
相关模式
- 复合:可以把复合结构的一条从叶子到根的路径作为一条责任链。
实现
using System;
namespace CoR
{
class Client
{
public interface IControl
{
void HandleHelp();
}
public abstract class Control : IControl
{
protected IControl parent;
public Control(IControl parent)
{
this.parent = parent;
}
public abstract void HandleHelp();
}
public class ControlA : Control
{
public ControlA(IControl parent) : base(parent) { }
public override void HandleHelp()
{
if (parent == null)
{
Console.WriteLine("无法处理该请求");
return;
}
Console.WriteLine("ControlA不能处理请求,将向下传递该请求");
parent.HandleHelp();
}
}
public class ControlB : Control
{
public ControlB(IControl parent) : base(parent) { }
public override void HandleHelp()
{
if (parent == null)
{
Console.WriteLine("无法处理该请求");
return;
}
Console.WriteLine("ControlB不能处理请求,将向下传递该请求");
parent.HandleHelp();
}
}
public class ControlC : Control
{
public ControlC(IControl parent) : base(parent) { }
public override void HandleHelp()
{
if (parent == null)
{
Console.WriteLine("无法处理该请求");
return;
}
Console.WriteLine("ControlC不能处理请求,将向下传递该请求");
parent.HandleHelp();
}
}
public class ControlD : Control
{
public ControlD(IControl parent) : base(parent) { }
public override void HandleHelp()
{
Console.WriteLine("ControlD能处理请求,将处理该请求");
Console.WriteLine("处理完成");
}
}
static void Main(string[] args)
{
Console.WriteLine("模拟无法处理的情景:");
var c3 = new ControlA(null);
var c2 = new ControlB(c3);
var c1 = new ControlC(c2);
c1.HandleHelp();
Console.WriteLine();
Console.WriteLine("模拟能够处理的场景:");
var C5 = new ControlD(null);
var C4 = new ControlB(C5);
var C3 = new ControlC(C4);
var C2 = new ControlA(C3);
var C1 = new ControlB(C2);
C1.HandleHelp();
}
}
}