The Chain of Responsibility is a key building block of extensible software.
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. – Gang of Four
Variations of this pattern are the basis for Servlet Filters, IIS Modules and Handlers and several open source projects I’ve had the opportunity to work with including Sync4J, JAMES, Log4Net, Unity and yes, even Joomla. It’s an essential tool in the OO toolbox and key in transforming rigid procedural code into a composable Domain Specific Language.
I’ve blogged about this pattern before so what’s new this time?
- The next filter in the chain is provided via a delegate parameter rather than a property
- The project is hosted on github
- There is a NuGet package for it
How does it work? It’s pretty simple, there is just one interface to implement and it looks like this:
1 2 3 4 |
|
Basically, you get an input to operate on and a value to return. The executeNext
parameter is a delegate for the next filter in the chain. The filters are composed together in a chain which is referred to as a Pipeline in the Tamarack framework. This structure is the essence of the Chain of Responsibility pattern and it facilitates some pretty cool things:
- Modify the input before the next filter gets it
- Modify the output of the next filter before returning
- Short circuit out of the chain by not calling the executeNext delegate
Show me examples!
Consider a block of code to process a blog comment coming from a web-based rich text editor. There are probably several things you’ll want to do before letting the text into your database.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
What about dependency injection for complex filters? Take a look at this user login pipeline. Notice the generic syntax for adding filters by type. Those filters are built-up using the supplied implementation of System.IServiceProvider
. My favorite is UnityServiceProvider
.
1 2 3 4 5 6 7 8 9 10 11 |
|
Here’s another place you might see the chain of responsibility pattern. Calculating the spam score of a block of text:
1 2 3 4 5 6 7 8 9 10 |
|
Prefer convention over configuration? Try this instead:
1 2 3 4 5 6 7 8 |
|
Let’s look at the IFilter
interface in action. In the spam score calculator example, each filter looks for markers in the text and adds to the overall spam score by modifying the result of the next filter before returning.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
In the login example, we look for the user in our local user store and if it exists we’ll short-circuit the chain and authenticate the request. Otherwise we’ll let the request continue to the next filter which looks for the user in an LDAP repository.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Why should I use it?
It’s pretty much my favorite animal. It’s like a lion and a tiger mixed… bred for its skills in magic. – Napoleon Dynamite
It’s simple and mildly opinionated in effort to guide you and your code into The Pit of Success. It’s easy to write single responsibility classes and use inversion of control and composition and convention over configuration and lots of other goodness. Try it out. Tell a friend.