The other day I yet again found myself using the Chain of Responsibility pattern to refactor some code that I had written a little earlier. It is one of my favorite patterns, primarily because it allows us to break a large piece of work into much smaller pieces which are far more easily re-used and unit tested.
I'm not going to describe the pattern itself as there is probably quite a lot of literature out there already. After all, it is one of the GoF patterns! The wiki page that I've linked to gives quite a good and practical description of it. Instead, I'm just going to quickly describe the recent scenario that popped up where I found the pattern useful.
We had rushed through a lot of coding to get a PoC out the door in 3 weeks. I wouldn't say it was the most ugly code that I've written, but certainly we didn't feel it was effective to write test cases (since the code was changing dramatically every day) and we didn't want to spend time refactoring until we knew what we were building would actually work! So, we certainly weren't using any design patterns initially.
However, after finishing the PoC I had a bit of time to go back and take stock of what we'd done. It was then that I found this particular piece of code which handled the servicing of a submitted Application (class/method names have been changed to protect the identity of their previous owners ... hahaha ):
public class Processor
{
private StateManager manager;
public void doSomething(BusinessApplication application)
{
manager.start(application);
manager.service(application);
manager.notify(application);
manager.queue(application);
manager.end(application);
}
}
I wouldn't say anything was particularly "horrible" about that piece of code. However, something is definitely odd. There were a few problems:
So i decided that a "chain of responsibility", or Handlers, should be introduced. I introduced a common interface:
public interface ApplicationHandler
{
void handle(BusinessApplication application);
}
... 5 individual implementations of this interface which dealt with the specific functionality it was concerned with:
public interface StartProcessingApplicationHandler
implements ApplicationHandler
{
// declare the specific dependencies of this handler
public StartProcessingApplicationHandler( ....... )
{
// assign the specific dependencies of this handler
}
void handle(BusinessApplication application)
{
// business logic for handling the start of the application
}
}
public interface ServiceApplicationHandler
implements ApplicationHandler
{
// declare the specific dependencies of this handler
public ServiceApplicationHandler( ....... )
{
// assign the specific dependencies of this handler
}
void handle(BusinessApplication application)
{
// business logic for handling the servicing of the application
}
}
//
// you get the idea....
//
... and to hook up these handlers in a pre-defined way to the Processor, I introduced a Factory for creating these applications in the correct order
public interface ApplicationHandlersFactory
{
List<ApplicationHandler> getHandlers()
{
return Arrays.asList(
new StartProcessingApplicationHandler( .... ),
new ServiceApplicationHandler( .... )
// and so on and so forth ....
);
}
}
One other good side-effect of breaking the workflow into smaller units of work is that they can be hooked up differently to alter the workflow, either by altering this factory or creating different factories. For example, if we wanted to ignore queueing and notification in another part of the system then it would be as simple as creating another factory which creates a chain of the following handlers: "starts -> services -> ends". The code for the handlers need not change, and need not know about these different workflows. This pattern has allowed us to make our code much more re-usable
The result was that the whole workflow was broken down into much smaller, more modular pieces that were far easier to unit test. Also, since each handler required different dependencies (pumped in through their constructor), this meant that a handler which processed the "start" of the Application did not have to be cluttered with dependencies that only a "service" handler required. And so on and so forth...
Finally, the Processor was refactored to this:
public class Processor
{
private ApplicationHandlersFactory factory;
public void doSomething(BusinessApplication application)
{
for( ApplicationHandler handler : factory.getHandlers() )
{
handler.handle(application);
}
}
}
So there you have it. A recent practical example of where the "chain of responsibility" pattern helped to break down a workflow into smaller parts. The benefits of which were:
Comments ...
I was thinking more like this:
https://github.com/alexwibowo/Pattern-Studio/blob/master/src/test/java/org/isolution/chainOfResponsibility/ChainOfResponsibilityClientTest.java
i.e. the client is not aware that there are a line of handlers.
anyway, the exact type of pattern that I described academic... the point is the polymorphism and modularity of the damn thing