![Chain of Responsibility](/images/patterns/cards/chain-of-responsibility-mini.png?id=36d85eba8d14986f053123de17aac7a7)
Chain of Responsibility を C# で
Chain of Responsibility は、 振る舞いに関するデザインパターンの一つで、 潜在的なハンドラーの連鎖の上を、 ハンドラーのどれかが処理するまで、 リクエストを回していきます。
このパターンを利用すると、 送り手のクラスと受け手の具象クラスとを結合することなく、 複数のオブジェクトにリクエストを処理する機会を与えることができます。 連鎖は実行時に、 標準のハンドラー・インターフェースに従うハンドラーから動的に構成されます。
複雑度:
人気度:
使用例: Chain of Responsibility パターンは、 C# ではよく見かけます。 フィルターやイベント・チェーンのようなオブジェクトの連鎖を対象に動作するコードを書く時に、 最も役に立ちます。
見つけ方: 共通のインターフェースに従うオブジェクトのグループで、 実作業を行うメソッドが、 別のオブジェクトの同一メソッドを呼ぶことから、 このパターンを識別できます。
概念的な例
この例は、 Chain of Responsibility デザインパターンの構造を説明するためのものです。 以下の質問に答えることを目的としています:
- どういうクラスからできているか?
- それぞれのクラスの役割は?
- パターンの要素同士はどう関係しているのか?
Program.cs: 概念的な例
using System;
using System.Collections.Generic;
namespace RefactoringGuru.DesignPatterns.ChainOfResponsibility.Conceptual
{
// The Handler interface declares a method for building the chain of
// handlers. It also declares a method for executing a request.
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
{
// The client code is usually suited to work with a single handler. In
// most cases, it is not even aware that the handler is part of a chain.
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)
{
// The other part of the client code constructs the actual chain.
var monkey = new MonkeyHandler();
var squirrel = new SquirrelHandler();
var dog = new DogHandler();
monkey.SetNext(squirrel).SetNext(dog);
// The client should be able to send a request to any handler, not
// just the first one in the chain.
Console.WriteLine("Chain: Monkey > Squirrel > Dog\n");
Client.ClientCode(monkey);
Console.WriteLine();
Console.WriteLine("Subchain: Squirrel > Dog\n");
Client.ClientCode(squirrel);
}
}
}
Output.txt: 実行結果
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.