구현 방법이야 여러 가지가 있겠습니다만, 기본적으로는 어떤 메시지를 지정된 핸들러들에게 전달해서, 처리할 수 있는 핸들러만 메시지를 처리하고 그렇지 못한 핸들러는 메시지를 무시하는 원리로 동작합니다.
아래 세 가지 구현을 예로 보여드립니다. 같은 내용을 영문 위키피디아(http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern) 에도 올려놓았습니다. 한국 위키피디아에는 아쉽게 해당 토픽이 없네요.
추상 클래스 기반 구현
using System;
namespace Chain_of_responsibility
{
public abstract class Chain
{
private Chain _next;
public Chain Next
{
get
{
return _next;
}
set
{
_next = value;
}
}
public void Message(object command)
{
if ( Process(command) == false && _next != null )
{
_next.Message(command);
}
}
public static Chain operator +(Chain lhs, Chain rhs)
{
Chain last = lhs;
while ( last.Next != null )
{
last = last.Next;
}
last.Next = rhs;
return lhs;
}
protected abstract bool Process(object command);
}
public class StringHandler : Chain
{
protected override bool Process(object command)
{
if ( command is string )
{
Console.WriteLine("StringHandler can handle this message : {0}",(string)command);
return true;
}
return false;
}
}
public class IntegerHandler : Chain
{
protected override bool Process(object command)
{
if ( command is int )
{
Console.WriteLine("IntegerHandler can handle this message : {0}",(int)command);
return true;
}
return false;
}
}
public class NullHandler : Chain
{
protected override bool Process(object command)
{
if ( command == null )
{
Console.WriteLine("NullHandler can handle this message.");
return true;
}
return false;
}
}
public class IntegerBypassHandler : Chain
{
protected override bool Process(object command)
{
if ( command is int )
{
Console.WriteLine("IntegerBypassHandler can handle this message : {0}",(int)command);
return false; // Always pass to next handler
}
return false; // Always pass to next handler
}
}
class TestMain
{
static void Main(string[] args)
{
Chain chain = new StringHandler();
chain += new IntegerBypassHandler();
chain += new IntegerHandler();
chain += new IntegerHandler(); // Never can reached
chain += new NullHandler();
chain.Message("1st string value");
chain.Message(100);
chain.Message("2nd string value");
chain.Message(4.7f); // not handled
chain.Message(null);
}
}
}
namespace Chain_of_responsibility
{
public abstract class Chain
{
private Chain _next;
public Chain Next
{
get
{
return _next;
}
set
{
_next = value;
}
}
public void Message(object command)
{
if ( Process(command) == false && _next != null )
{
_next.Message(command);
}
}
public static Chain operator +(Chain lhs, Chain rhs)
{
Chain last = lhs;
while ( last.Next != null )
{
last = last.Next;
}
last.Next = rhs;
return lhs;
}
protected abstract bool Process(object command);
}
public class StringHandler : Chain
{
protected override bool Process(object command)
{
if ( command is string )
{
Console.WriteLine("StringHandler can handle this message : {0}",(string)command);
return true;
}
return false;
}
}
public class IntegerHandler : Chain
{
protected override bool Process(object command)
{
if ( command is int )
{
Console.WriteLine("IntegerHandler can handle this message : {0}",(int)command);
return true;
}
return false;
}
}
public class NullHandler : Chain
{
protected override bool Process(object command)
{
if ( command == null )
{
Console.WriteLine("NullHandler can handle this message.");
return true;
}
return false;
}
}
public class IntegerBypassHandler : Chain
{
protected override bool Process(object command)
{
if ( command is int )
{
Console.WriteLine("IntegerBypassHandler can handle this message : {0}",(int)command);
return false; // Always pass to next handler
}
return false; // Always pass to next handler
}
}
class TestMain
{
static void Main(string[] args)
{
Chain chain = new StringHandler();
chain += new IntegerBypassHandler();
chain += new IntegerHandler();
chain += new IntegerHandler(); // Never can reached
chain += new NullHandler();
chain.Message("1st string value");
chain.Message(100);
chain.Message("2nd string value");
chain.Message(4.7f); // not handled
chain.Message(null);
}
}
}
추상 클래스 기반으로 구현하는 방법입니다. 하지만 이 방식은 썩 좋지 않은 방식입니다. 일단 클래스 상속을 꼭 해야 되기 때문에 파생 클래스를 핸들러로 등록할 수 없습니다.
그 외에 ArrayList라는 좋은 컬렉션 클래스가 있는데 굳이 링크드리스트를 구현해서 사용하고 있는 것도 눈여겨 보아 주시기 바랍니다.
인터페이스 기반 구현
using System;
using System.Collections;
namespace Chain_of_responsibility
{
public interface IChain
{
bool Process(object command);
}
public class Chain
{
private ArrayList _list;
public ArrayList List
{
get
{
return _list;
}
}
public Chain()
{
_list = new ArrayList();
}
public void Message(object command)
{
foreach ( IChain item in _list )
{
bool result = item.Process(command);
if ( result == true ) break;
}
}
public void Add(IChain handler)
{
List.Add(handler);
}
}
public class StringHandler : IChain
{
public bool Process(object command)
{
if ( command is string )
{
Console.WriteLine("StringHandler can handle this message : {0}",(string)command);
return true;
}
return false;
}
}
public class IntegerHandler : IChain
{
public bool Process(object command)
{
if ( command is int )
{
Console.WriteLine("IntegerHandler can handle this message : {0}",(int)command);
return true;
}
return false;
}
}
public class NullHandler : IChain
{
public bool Process(object command)
{
if ( command == null )
{
Console.WriteLine("NullHandler can handle this message.");
return true;
}
return false;
}
}
public class IntegerBypassHandler : IChain
{
public bool Process(object command)
{
if ( command is int )
{
Console.WriteLine("IntegerBypassHandler can handle this message : {0}",(int)command);
return false; // Always pass to next handler
}
return false; // Always pass to next handler
}
}
class TestMain
{
static void Main(string[] args)
{
Chain chain = new Chain();
chain.Add(new StringHandler());
chain.Add(new IntegerBypassHandler());
chain.Add(new IntegerHandler());
chain.Add(new IntegerHandler()); // Never can reached
chain.Add(new NullHandler());
chain.Message("1st string value");
chain.Message(100);
chain.Message("2nd string value");
chain.Message(4.7f); // not handled
chain.Message(null);
}
}
}
using System.Collections;
namespace Chain_of_responsibility
{
public interface IChain
{
bool Process(object command);
}
public class Chain
{
private ArrayList _list;
public ArrayList List
{
get
{
return _list;
}
}
public Chain()
{
_list = new ArrayList();
}
public void Message(object command)
{
foreach ( IChain item in _list )
{
bool result = item.Process(command);
if ( result == true ) break;
}
}
public void Add(IChain handler)
{
List.Add(handler);
}
}
public class StringHandler : IChain
{
public bool Process(object command)
{
if ( command is string )
{
Console.WriteLine("StringHandler can handle this message : {0}",(string)command);
return true;
}
return false;
}
}
public class IntegerHandler : IChain
{
public bool Process(object command)
{
if ( command is int )
{
Console.WriteLine("IntegerHandler can handle this message : {0}",(int)command);
return true;
}
return false;
}
}
public class NullHandler : IChain
{
public bool Process(object command)
{
if ( command == null )
{
Console.WriteLine("NullHandler can handle this message.");
return true;
}
return false;
}
}
public class IntegerBypassHandler : IChain
{
public bool Process(object command)
{
if ( command is int )
{
Console.WriteLine("IntegerBypassHandler can handle this message : {0}",(int)command);
return false; // Always pass to next handler
}
return false; // Always pass to next handler
}
}
class TestMain
{
static void Main(string[] args)
{
Chain chain = new Chain();
chain.Add(new StringHandler());
chain.Add(new IntegerBypassHandler());
chain.Add(new IntegerHandler());
chain.Add(new IntegerHandler()); // Never can reached
chain.Add(new NullHandler());
chain.Message("1st string value");
chain.Message(100);
chain.Message("2nd string value");
chain.Message(4.7f); // not handled
chain.Message(null);
}
}
}
추상 클래스 구현보다 좀 더 유연해졌습니다. 인터페이스를 기반으로 구현했기 때문에 파생 클래스를 핸들러로 등록할 수도 있게 되었습니다. 또한 ArrayList라는 컬렉션을 사용했지요.
델리게이트와 이벤트를 사용한 구현
using System;
namespace Chain_of_responsibility
{
public class StringHandler
{
private static int count;
public void Process(object command)
{
if ( command is string )
{
string th;
count++;
if ( count % 10 == 1 ) th = "st";
else if ( count % 10 == 2 ) th = "nd";
else if ( count % 10 == 3 ) th = "rd";
else th = "th";
Console.WriteLine("StringHandler can handle this {0}{1} message : {2}",count,th,(string)command);
}
}
}
public class StringHandler2
{
public void Process(object command)
{
if ( command is string )
{
Console.WriteLine("StringHandler2 can handle this message : {0}",(string)command);
}
}
}
public class IntegerHandler
{
public void Process(object command)
{
if ( command is int )
{
Console.WriteLine("IntegerHandler can handle this message : {0}",(int)command);
}
}
}
class Chain
{
public delegate void MessageHandler(object message);
public static event MessageHandler Message;
public static void Process(object message)
{
Message(message);
}
}
class TestMain
{
static void Main(string[] args)
{
StringHandler sh1 = new StringHandler();
StringHandler2 sh2 = new StringHandler2();
IntegerHandler ih = new IntegerHandler();
Chain.Message += new Chain.MessageHandler(sh1.Process);
Chain.Message += new Chain.MessageHandler(sh2.Process);
Chain.Message += new Chain.MessageHandler(ih.Process);
Chain.Message += new Chain.MessageHandler(ih.Process); // Can also reached
Chain.Process("1st string value");
Chain.Process(100);
Chain.Process("2nd string value");
Chain.Process(4.7f); // not handled
}
}
}
namespace Chain_of_responsibility
{
public class StringHandler
{
private static int count;
public void Process(object command)
{
if ( command is string )
{
string th;
count++;
if ( count % 10 == 1 ) th = "st";
else if ( count % 10 == 2 ) th = "nd";
else if ( count % 10 == 3 ) th = "rd";
else th = "th";
Console.WriteLine("StringHandler can handle this {0}{1} message : {2}",count,th,(string)command);
}
}
}
public class StringHandler2
{
public void Process(object command)
{
if ( command is string )
{
Console.WriteLine("StringHandler2 can handle this message : {0}",(string)command);
}
}
}
public class IntegerHandler
{
public void Process(object command)
{
if ( command is int )
{
Console.WriteLine("IntegerHandler can handle this message : {0}",(int)command);
}
}
}
class Chain
{
public delegate void MessageHandler(object message);
public static event MessageHandler Message;
public static void Process(object message)
{
Message(message);
}
}
class TestMain
{
static void Main(string[] args)
{
StringHandler sh1 = new StringHandler();
StringHandler2 sh2 = new StringHandler2();
IntegerHandler ih = new IntegerHandler();
Chain.Message += new Chain.MessageHandler(sh1.Process);
Chain.Message += new Chain.MessageHandler(sh2.Process);
Chain.Message += new Chain.MessageHandler(ih.Process);
Chain.Message += new Chain.MessageHandler(ih.Process); // Can also reached
Chain.Process("1st string value");
Chain.Process(100);
Chain.Process("2nd string value");
Chain.Process(4.7f); // not handled
}
}
}
이 방법이 C#에서 이벤트 처리를 위해 주로 사용하는 방식이라 할 수 있습니다.
인터페이스조차 사용하지 않았지요.
델리게이트는 일종의 함수 포인터와 비슷한 개념인데, 좀 더 객체지향적이고 좀 더 안전한 타입입니다. 이벤트는 델리게이트를 좀 더 편하게 사용하기 위한 C# 언어 차원의 배려이고요. 이벤트 키워드 없이 델리게이트만으로도 위의 코드를 사용할 수 있습니다.
Posted by 운명과시간의신

