Friday, September 11, 2009

Design Patterns – Chain of Responsibility Pattern

Imagine you have complicated decision logic and you’ve got a big if-then-else like structure in your code which you want to simplify.
The Chain of Responsibility Pattern is a good way for code refactoring in such situation. It will make the code more flexible and easy to support and modify in the future.

You prepare a set of interdependent chain handler classes linked in a chain. Every chain handler class implements part of the decision logic and has a link to next chain handler object. A request data object is passed thru the chain. Eventually one of the handlers on the chain matches some decision criteria, does data processing and ends up the run flow. This would be the handler objects in the chain that takes the responsibility and does data processing having received the request data object.

In this example we will try to programmatically come up with a conclusion based on passed in to the chain two boolean parameters. For every possible combination of those parameters we are going give a conclusion back, so we have four different possible results. Otherwise we could do things using conditional structures like:



If Param1 is true
If Param2 is true
Conclusion1
Else
Conclusion2
Else
If Param2 is true
Conclusion3
Else
Conclusion4


Based on that conditional structure we will create chain of responsibility with four different chain handler classes covering all decision making combinations.

We will use C# as a programming language for the examples.

Chain Handler Interface and Base Class



We start with the definition of the chain handler interface. The chain handler object stores internally a request data object. We have a way to set if there is a handler next on the chain. The handler has Run() void where the decision making and eventually data processing will be done.



// All handler objects implement common interface.
// T: request object- holds data to process
// and the result of the processing
public interface IChainHandler<TRequest>
{
IChainHandler<TRequest> SetNextLink(IChainHandler<TRequest> next); // set link to next handler
void Run(); // does processing
}



As we will have four chain handler classes and they all have a lot in common, we create parent abstract base class for chain handler.



// base class for all concrete chain handler objects
public abstract class ChainHandlerBase : IChainHandler<RequestData>
{
// the request data object
protected RequestData _requestData;

// a reference to next chain handler object
private IChainHandler<RequestData> _nextHandler;

// hide parameterless constructor
private ChainHandlerBase() { }

// expose constructor with parameters
public ChainHandlerBase(RequestData requestData)
{
_requestData = requestData;
}
// set the reference to the next chain handler object
public IChainHandler<RequestData> SetNextLink(IChainHandler<RequestData> nextHandler)
{
_nextHandler = nextHandler;
return _nextHandler;
}

// let the concrete implementation make processing decision
public abstract void Run();

// run next handler in the chain if defined or end up the processing
protected void RunNext()
{
if (_nextHandler != null)
_nextHandler.Run();
}

}



Request Data Object



A step back – we need to define the request data object which we’ll pass thru the chain. This object holds two input boolean parameters and a holder where we write the conclusion message back.



// Data to pass thru the chain.
// Based on this data the chain
public class RequestData
{
// pass in parameters
public bool HaveLotOfMoney { get; set; }
public bool HaveBrilliantIdea { get; set; }

// a message to send back
public string MessageBack { get; set; }
}




Concrete Chain Handler Implementations



Now we are ready to define concrete chain handler classes and cover all possible combinations of the input parameters.



// concrete chain handler object
public class ChainHandlerOne : ChainHandlerBase
{
public ChainHandlerOne(RequestData requestData) : base(requestData) { }
public override void Run()
{
if ((_requestData.HaveLotOfMoney) && (_requestData.HaveBrilliantIdea))
{
_requestData.MessageBack = "I am with you.";
return;
}
RunNext();
}
}

// concrete chain handler object
public class ChainHandlerTwo : ChainHandlerBase
{
public ChainHandlerTwo(RequestData requestData) : base(requestData) { }
public override void Run()
{
if ((_requestData.HaveLotOfMoney) && (!_requestData.HaveBrilliantIdea))
{
_requestData.MessageBack = "Well done. Go on pension.";
return;
}
RunNext();
}
}

// concrete chain handler object
public class ChainHandlerThree : ChainHandlerBase
{
public ChainHandlerThree(RequestData requestData) : base(requestData) { }
public override void Run()
{
if ((!_requestData.HaveLotOfMoney) && (_requestData.HaveBrilliantIdea))
{
_requestData.MessageBack = "What a nice idea.";
return;
}
RunNext();
}
}

// concrete chain handler object
public class ChainHandlerFour : ChainHandlerBase
{
public ChainHandlerFour(RequestData requestData) : base(requestData) { }
public override void Run()
{
if ((!_requestData.HaveLotOfMoney) && (!_requestData.HaveBrilliantIdea))
{
_requestData.MessageBack = "Don't give up.";
return;
}
RunNext();
}
}




Set Up the Chain



We are ready at this point to put in order the chain. We create chain handler objects and link them in a appropriate order. We send to the first handler in the chain a request data object and hit the Run() method to get the result calculated.



// wraps the chain initialization and processing code
static RequestData WhatAboutMe(RequestData requestData)
{
// construct the first chain handler.
ChainHandlerBase firstHandler = new ChainHandlerOne(requestData);

// arrange chain of responsibility as list of chain handker objects
firstHandler
.SetNextLink(new ChainHandlerTwo(requestData))
.SetNextLink(new ChainHandlerThree(requestData))
.SetNextLink(new ChainHandlerFour(requestData));

// start processing of the request data
firstHandler.Run();

// return back the request data with the message created
return requestData;
}



Run the Chain



The win of utilizing chain responsibility pattern is the extensibility of our code in future. If we need additional logic we have to implement new chain handler class. We also may construct the chains in one or another way, we can order the chain handler object based on concrete needs. We absolutely take care of single responsibility principle and we divide a long and complicated to maintain code into well testable small classes.

Finally let’s run our chain of responsibility implementation. We’ll send every possible combination of input to the chain and see what’ll happen.




static void Main(string[] args)
{
Console.WriteLine(WhatAboutMe(new RequestData {
HaveLotOfMoney = true, HaveBrilliantIdea = true }).MessageBack);

Console.WriteLine(WhatAboutMe(new RequestData {
HaveLotOfMoney = true, HaveBrilliantIdea = false }).MessageBack);

Console.WriteLine(WhatAboutMe(new RequestData {
HaveLotOfMoney = false, HaveBrilliantIdea = true }).MessageBack);

Console.WriteLine(WhatAboutMe(new RequestData {
HaveLotOfMoney = false, HaveBrilliantIdea = false }).MessageBack);

Console.Write("Hit Enter to finish.");
Console.ReadLine();
}



Running that we get on the console like:



Atanas Hristov

kick it on DotNetKicks.com
Shout it

No comments:

Post a Comment