
C#으로 작성된 책임 연쇄
책임 연쇄 패턴은 핸들러 중 하나가 요청을 처리할 때까지 핸들러들의 체인(사슬)을 따라 요청을 전달할 수 있게 해주는 행동 디자인 패턴입니다.
이 패턴은 발신자 클래스를 수신자들의 구상 클래스들에 연결하지 않고도 여러 객체가 요청을 처리할 수 있도록 합니다. 체인은 표준 핸들러 인터페이스를 따르는 모든 핸들러와 런타임 때 동적으로 구성될 수 있습니다.
복잡도:
인기도:
사용 예시들: 책임 연쇄 패턴은 C 코드에 매우 일반적이며, 당신의 코드가 필터, 이벤터 체인 등과 같은 객체 체인과 함께 작동할 때 특히 유용합니다.
식별: 패턴의 모든 객체는 공통 인터페이스를 따르며, 다른 객체들의 같은 메서드들을 간접적으로 호출하는 한 객체 그룹의 행동 메서드들이 있습니다.
개념적인 예시
이 예시는 책임 연쇄 패턴의 구조를 보여주고 다음 질문에 중점을 둡니다:
- 패턴은 어떤 클래스들로 구성되어 있나요?
- 이 클래스들은 어떤 역할을 하나요?
- 패턴의 요소들은 어떻게 서로 연관되어 있나요?
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.