Code Looks Like a Chain
As dependency injections have changed the game in recent years, they've also provided a chance to update classic code patterns, like the Chain of Responsibility.
Join the DZone community and get the full member experience.
Join For FreeAs Aerosmith sang many years ago, “Code looks like a Chain.” No? They didn't say that? Maybe I'm remembering it wrong. Well, as you might have guessed, in this post, we'll talk about the Chain of Responsibility design pattern. This pattern is not one of the more popular ones, at least among the patterns defined by the Gang of Four, but modern dependency injection frameworks give us the possibility to implement it in a smart, and fresh way. Let’s see how.
Introduction
Disclaimer: There is nothing new with this pattern. A colleague of mine used it days ago, and I've also used it a lot in the past. This post was inspired by a solution to a problem I recently encountered, which we'll dive into below, and I did not immediately notice that this pattern could be used to solve that problem.
The Classic Pattern
The Chain of Responsibility pattern is a behavioral design pattern. It was first defined in the book Design Patterns, written by the Gang of Four. The intent of the pattern is to:
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.
The class diagram relative to the pattern is as follows:
Loose coupling is accomplished through the definition of a standard interface by which we can respond to clients’ requests. In the above diagram, it is represented by the abstract type Handler
. The ability to have more than one object respond to a request is simply accomplished by creating a chain of objects that implement the above interface. Each object owns an instance of the next link in the chain. The successor
attribute satisfies this scope.
When called, each handler verifies whether it is able to respond to the request. If it can, it performs the operations requested. At this point, we can have many different implementations, which differ by the request forward policy. We can implement a policy that stops the request in the chain once a ConcreteHandler
has stated that can handle it. In this case, the implementation of the handleRequest
method will be the following:
if (/* The request can be successfully handled */) {
// Handle the request
} else {
successor.handleRequest(request);
}
On the other side, we can forward the request to the next handler in the chain, whether the current handler is able to fulfill it or not.
if (/* The request can be successfully handled */) {
// Handle the request
}
successor.handleRequest(request);
The building process of the chain will be something similar to the following.
Handler chain = new ConcreteHandler1(new ConcreteHandler2(new ConcreteHandler3()));
chain.handleRequest(request);
Inside the JDK implementation, the pattern is applied in at least two points:
- Implementation of the logging mechanisms:
java.util.logging.Logger#log()
- Implementation of the filtering mechanisms of http requests and responses in the Servlet specification:
javax.servlet.Filter#doFilter()
The Advent of Dependency Injection
As in many other situations, the definition of the dependency injection pattern changed the tune. Let’s see how to use DI features to modernize the Chain of Responsibility pattern.
First of all, we need a feature that, in some ways, all the DI libraries implement: multibindings. Basically, using this feature, it is possible to provide an instance of all subtypes of a type, simply by trying to inject a collection of that type.
Let’s have, for example, the following type system.
interface Shop {}
class PetShop implements Shop {}
class Grocery implements Shop {}
class Drugstore implements Shop {}
// And so on...
Now, we will define a new type ShoppingCenter
, that owns an instance of all subtypes of Shop
. Using dependency injection, we can achieve these goals simply by injecting a collection of Shop
inside ShoppingCenter
.
class ShoppingCenter {
private final Set<Shop> shops;
@Inject
public void ShoppingCenter(Set<Shop> shops) {
this.shops = shops;
}
// Class body using shops
}
Simple as f**k! Obviously, every dependency injection library has its own ways to configure the injector to resolve such a situation. In Spring, using the auto-discovery feature, you have to do very little configuration. In Guice, the story is a little more complicated, but the final result is the same.
Modern Implementations of CoR
To summarize a little: We have seen the Chain of Responsibility design pattern in its classical form; we have seen the multibindings feature provided by dependency injection libraries; in the final step to nirvana, we will see how to mix these two concepts together.
First of all, we need a slightly different implementation of the original CoR design pattern. Let’s introduce a new type, the ChainHandler
. The responsibility of this type is to own the whole chain and to expose a single point of access to the functions offered by the chain itself to the clients.
class ChainHandler {
private final Set<Handler> handlers;
@Inject
public void ChainHandler(Set<Handler> handlers) {
this.handlers = handlers;
}
// Pass the request to each handler of the chain
public void handle(final Request request) {
handlers.forEach(h -> h.handle(request));
}
}
Taking advantage of dependency injection, adding a new Handler
implementation requires no changes in the existing code at all. This means that virtually no regression tests need to be performed. On the other hand, it is a little bit more difficult (but not impossible) to impose an order of execution of Handler
s inside the chain.
Warnings
As in many other patterns, it is important to focus on what the roles of the classes that build the pattern are. Which responsibilities will you give to a concrete Handler
? Will you develop the business logic of the application directly inside the body of that Handler
?
At first, many of us would provide the above solution. It is not inherently wrong. However, this kind of design limits the reusability of the code and violates the famous Single Responsibility Principle.
For example, let’s imagine that we have to implement a system that enriches the information of a financial transaction. The enrichment process is developed using the CoR pattern. One of the possible enrichments could be the insertion of the payee country derived from an IBAN or a BIC code. Then, let’s defined a CountryPayeeEnricher
.
At a first glance, one could be tempted to insert the code that extracts the country information directly inside the body of the CountryPayeeEnricher
class. But what if we have to reuse this function in another point of our application (or in another application at all)? It is a far better solution to resume the composition principle, putting the code inside a dedicated type, let’s say PayeeService
.
class PayeeService {
public Country deriveCountryFromPayee(String payee) {
// Code that extract the country information from the
// input payess
}
// Omissis...
}
class CountryPayeeEnricher implements Enrichment {
private PayeeService payeeService;
@Inject
public void CountryPayeeEnricher(PayeeService payeeService) {
this.payeeService = payeeService;
}
public void handle(Transaction tx) {
Country country = payeeService.deriveCountryFromPayee(tx.getPayee());
tx.setCountry(country);
// ...or something like this
}
}
In this way, we end up with two types with different responsibilities: There's the PayeeService
type, which offers reusable services directly connected to payee information, and the CountryPayeeEnricher
type, which offers standardized access to the services offered by the previous type.
The Scala Way
For the sake of completeness, I also want to talk about the implementation of the CoR pattern in the Scala language. As with many other design patterns, there is an implementation of the CoR pattern built into the language: partial functions. As the theory states, a partial function is a function that is defined only for some subset of values of its domain. In Scala, such functions have a specific type — PartialFunction[T, V]
.
Partial functions in Scala are defined using pattern matching statements. In the following example, the value fraction
is not defined for a zero value.
val fraction: PartialFunction[Int, Int] = {
case d: Int if d != 0 => 42 / d
}
If there are multiple definition sets, you can have more than one case
clause. If you think of every case
clause as a condition to satisfy in order to apply a function (the handler in CoR, do you see it?), you get the CoR pattern again.
case class Request(val value: String) { /* ... */ }
val someStupidFunction: PartialFunction[Request, String] = {
case Request(42) => "The final answer"
case Request(0) => "You know nothing, John Snow"
case Request(666) => "Something strange is going on in here"
//. ..
}
Then, a partial function can be thought as a simple chain of handlers. Clearly, there are some additional constraints you have to fulfill to use CoR in such a way. In fact:
You won’t be able to store any metadata on each handler.
You can’t remove handlers from the chain.
You can’t inspect the handler to display or pretty-print it.
If you do not need to do these things, pattern-matching PartialFunctions work great.
Published at DZone with permission of Riccardo Cardin, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Stack in Data Structures
-
IDE Changing as Fast as Cloud Native
-
Understanding the Role of ERP Systems in Modern Software Development
-
Auditing Tools for Kubernetes
Comments