Chain of Responsibility is one among the 23 Design Patterns by Gang of Four. It
is an interesting pattern and similar to the Observer pattern. In this article we
are exploring the Chain of Responsibility pattern.
I am using the same Challenge and Solution style for explaining this pattern.
Challenge
You are working on an application in which the Logic class generates various
messages. The messages are of two types.
- Normal Priority
- High Priority
The Normal Priority messages are to be
processed by Logger class and High Priority messages by Emailer class.
You have to make the design in such a way that the Logic class need not think
about right handler of the message. It will just send the message.
How the design will proceed?
Definition
"Avoid coupling the sender of a request to its receiver by giving more than one
object a chance to handle the request. Chain the receiving objects and pass the
request along the chain until an object handles it."
Control Flow
Implementation
Following is the definition for the Logic class:
public
class Logic
{
public IReceiver
Receiver;
public void CreateMessage(Message
message)
{
if (Receiver !=
null)
Receiver.HandleMessage(message);
}
}
We can see from the above code that the Logic class has a property of type
IReceiver. On the CreateMessage method this Receiver is used to handle the
message. So only one receiver is registered by the Logic class instance.
Following are the definition of Message class and MessagePriority enumeration:
public
class Message
{
public string
Text;
public
MessagePriority Priority;
}
public
enum MessagePriority
{
Normal,
High
}
Following are the Receiver Interface and Implementation classes:
public
interface IReceiver
{
bool HandleMessage(Message
message);
}
public
class Logger
: IReceiver
{
private
IReceiver _nextReceiver;
public Logger(IReceiver nextReceiver)
{
_nextReceiver = nextReceiver;
}
public bool HandleMessage(Message
message)
{
if (message.Priority ==
MessagePriority.Normal)
{
Trace.WriteLine(message.Text +
" : Logger processed it!");
return true;
}
else
{
if (_nextReceiver !=
null)
_nextReceiver.HandleMessage(message);
}
return false;
}
}
public
class Emailer
: IReceiver
{
private
IReceiver _nextReceiver;
public Emailer(IReceiver nextReceiver)
{
_nextReceiver = nextReceiver;
}
public bool HandleMessage(Message
message)
{
if (message.Priority ==
MessagePriority.High)
{
Trace.WriteLine(message.Text +
" : Emailer processed it!");
return true;
}
else
{
if (_nextReceiver !=
null)
_nextReceiver.HandleMessage(message);
}
return false;
}
}
From the above code we can see that each receiver class checks the message
priority and processes it. If the priority is not matching it is send to the
next receiver in the chain.
Here each receiver takes care of passing the unprocessed message. The next
receiver is stored through the constructor of each receiver.
The chain will be executed until:
Message is processed
Receiver chain exhausted
Code Execution
The windows forms application attached can be used to test the control flow.
Here the chain is built as following:
Logic class holds first receiver which is Logger
Logger class holds the next receiver which is Emailer
Emailer class have null receiver denoting end of chain
The following code depicts the above chain creation:
Logic
logic = new Logic();
logic.Receiver = new
Logger(new
Emailer(null));
Note: Please note that the advantage of this pattern is decoupling of
sender and receiver. The sender does not think about the right receiver. Instead
it will pass the request and the appropriate receiver should process it. The
above example can be written using a list of receivers in the Logic class with a
Boolean property mentioning whether the message is processed or not.
Comparing Observer and Chain of Responsibility
In the case of Observer pattern all the registered receivers will get the
request. Each of the receivers is interested in processing it. But in chain of
responsibility the request is passed until it is not processed.
Summary
In this article we have explored Chain of Responsibility pattern with a C#
example. The associated source code contains the example we discussed.