旧游无处不堪寻
无寻处,惟有少年心
设计模式-责任链

责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,允许你将请求沿着处理者链进行发送。收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下个处理者。

解释


与许多其他行为设计模式一样,责任链会将特定行为转换为被称作处理者的独立对象。
模式建议你将这些处理者连成一条链。链上的每个处理者都有一个成员变量来保存对于下一处理者的引用。除了处理请求外,处理者还负责沿着链传递请求。请求会在链上移动,直至所有处理者都有机会对其进行处理。

最重要的是: 处理者可以决定不再沿着链传递请求,这可高效地取消所有后续处理步骤

不过还有一种稍微不同的方式(也是更经典一种),那就是处理者接收到请求后自行决定是否能够对其进行处理。如果自己能够处理,处理者就不再继续传递请求。因此在这种情况下,每个请求要么最多有一个处理者对其进行处理,要么没有任何处理者对其进行处理。在处理图形用户界面元素栈中的事件时,这种方式非常常见。

责任链模式 UML


适用场景


  1. 当程序需要使用不同方式处理不同种类请求,而且请求类型和顺序预先未知时,可以使用责任链模式
  2. 当必须按顺序执行多个处理者时,可以使用该模式
  3. 如果所需处理者及其顺序必须在运行时进行改变,可以使用责任链模式

代码示例


using System;
namespace ChainOfResponsibilityPatternDemo
{
public interface IHandler
{
IHandler SetNext(IHandler handler);

object Handle(object request);
}

// The default chaining behavior can be implemented inside a base handler
// class.
abstract class AbstractHandler : IHandler
{
private IHandler _nextHandler;

public IHandler SetNext(IHandler handler)
{
this._nextHandler = handler;

// Returning a handler from here will let us link handlers in a
// convenient way like this:
// monkey.SetNext(squirrel).SetNext(dog);
return handler;
}

public virtual object Handle(object request)
{
if (this._nextHandler != null)
{
return this._nextHandler.Handle(request);
}
else
{
return null;
}
}
}

class MonkeyHandler : AbstractHandler
{
public override object Handle(object request)
{
if ((request as string) == "Banana")
{
return $"Monkey: I'll eat the {request.ToString()}.\n";
}
else
{
return base.Handle(request);
}
}
}

class SquirrelHandler : AbstractHandler
{
public override object Handle(object request)
{
if (request.ToString() == "Nut")
{
return $"Squirrel: I'll eat the {request.ToString()}.\n";
}
else
{
return base.Handle(request);
}
}
}

class DogHandler : AbstractHandler
{
public override object Handle(object request)
{
if (request.ToString() == "MeatBall")
{
return $"Dog: I'll eat the {request.ToString()}.\n";
}
else
{
return base.Handle(request);
}
}
}

class Client
{
public static void ClientCode(AbstractHandler handler)
{
foreach (var food in new List<string> { "Nut", "Banana", "Cup of coffee" })
{
Console.WriteLine($"Client: Who wants a {food}?");

var result = handler.Handle(food);

if (result != null)
{
Console.Write($" {result}");
}
else
{
Console.WriteLine($" {food} was left untouched.");
}
}
}
}

class Program
{
static void Main(string[] args)
{
var monkey = new MonkeyHandler();
var squirrel = new SquirrelHandler();
var dog = new DogHandler();

monkey.SetNext(squirrel).SetNext(dog);

Console.WriteLine("Chain: Monkey > Squirrel > Dog\n");
Client.ClientCode(monkey);
Console.WriteLine();

Console.WriteLine("Subchain: Squirrel > Dog\n");
Client.ClientCode(squirrel);
// output

// Chain: Monkey > Squirrel > Dog

// Client: Who wants a Nut?
// Squirrel: I'll eat the Nut.
// Client: Who wants a Banana?
// Monkey: I'll eat the Banana.
// Client: Who wants a Cup of coffee?
// Cup of coffee was left untouched.

// Subchain: Squirrel > Dog

// Client: Who wants a Nut?
// Squirrel: I'll eat the Nut.
// Client: Who wants a Banana?
// Banana was left untouched.
// Client: Who wants a Cup of coffee?
// Cup of coffee was left untouched.
}
}
}