I am implementing a chain of responsibility pattern.
I have different policies that can be combined in a list and I have a Processor that processes the list of policies. Each policy can process the CustomInput and can choose if the rest of the policies should be processed too.
interface Policy {
public boolean process(CustomInput input);
}
interface Processor {
public void process(List<Policy> policies, CustomInput input)
}
I was going to implement the Processor looping over the list of policies and checking the boolean result of each to know whether to continue with the rest of policies.
My colleague suggested to pass the next Policy to each Policy and let them call (or not) the next one (like FilterChain does for example).
My question is the following:
Is there any benefits I don't see in the second solution (passing the next policy to the currently processing one) over looping over each policy and checking it's result?
Chain of responsibility pattern is used to achieve loose coupling in software design where a request from client is passed to a chain of objects to process them.
Chain of Responsibility is a behavioral design pattern that lets you pass requests along a chain of handlers. Upon receiving a request, each handler decides either to process the request or to pass it to the next handler in the chain.
The Chain of Responsibility is a behavioral design pattern that processes a request through a series of processor (handlers/receivers) objects. The request is sent from one handler object to another and processed by one (pure implementation) or all of the handlers. All the handlers are part of the chain.
Consequences. Chain of Responsibility has the following benefits and liabilities: Reduced coupling. The pattern frees an object from knowing which other object handles a request.
Could you not implement a half-way solution that allows both components to have part control?
interface Policy {
public Policy process(CustomInput input, Policy defaultNext);
}
process
could then default to returning defaultNext
unless it has its own choice.
The idea of passing the next one along makes no sense to me. So I want a chain of:
A - B - C - D
How does C know about D? If it's in the code for C, then any change to the chain is going to be a huge hassle to implement.
The chain needs to either follow some other path that already exists, as for instance responders do when they just bubble up requests for help each to its respective parent (the example in the Gang of Four book), or you need to construct the chain, which is why at the bottom of the section in Go4, they mention the Composite Pattern as a naturally occurring accomplice.
Note also that one of the main reasons for doing Chain of Responsibility is when the types that might operate on the item are different. Which makes implementing it with an interface in Java perfect.
To answer your main question: the benefit of using Chain of Responsibility in this case is two fold: 1. you are not making a god object that knows about all things that could ever happen to achieve the goal (the successful construction of a Policy), and 2. you are not having to put in a lot of ugly checking code to see when you have reached terminus, because whoever handles it, by not calling its successor, will prompt the return of the finished item.
I am implementing a chain of responsibility pattern.
Not really. Your colleague's suggestion is actually how the GoF define Chain of Responsibility.
The first object in the chain receives the request and either handles it or forwards it to the next candidate on the chain, which does likewise... each object on the chain shares a common interface for handling requests and for accessing its successor on the chain. (page 224)
Chain of Responsibility can simplify object interconnections. Instead of objects maintaining references to all candidate receivers, they keep a single reference to their successor. (page 226)
Clearly CoR is defined as a singly-linked list. What you describe is more like the Mediator Pattern, with your Processor
playing the mediator role. The tradeoff is centralized vs. decentralized control.
It centralizes control. The Mediator pattern trades complexity of interaction for complexity in the mediator. Because a mediator encapsulates protocols, it can become more complex than any individual colleague. This can make the mediator itself a monolith that's hard to maintain. (page 277)
If you're confident the Processor
will never do more than iterate over a list and check a boolean value, then I think your design is sound. The danger is that the Processor
might acquire "special" logic related to a specific CustomInput
or Policy
. That's a slippery slope to a god class.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With