Chain of responsibility: loop or next?
Asked Answered
A

3

12

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?

Autosuggestion answered 3/12, 2013 at 14:52 Comment(4)
in the first case, a top operator is responsible for applying all policies. in the second case, each policy must know its sub-policy and can decide to apply it or not. It's a matter of where you place the responsibility to apply the policies.Sarmatia
There are cases when a pattern like this becomes overhead, this should be considered too. Whilst CoR is nice to use, don't forget to KISS. There are cases and cases to be considered.Brianabriand
@Matheus, in my case I do need it (the list of policies come from somewhere another service and the processing code has no knowledge of what they do, only they must be processed before it can continue with next step).Autosuggestion
Unless there is a purpose to it, coupling the iteration and selection steps with the processing steps seems to be unnecessary coupling.Advert
B
3

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.

Blouson answered 3/12, 2013 at 16:4 Comment(1)
I tend to agree with you Rob, I don't like the fact a policy has to have knowledge of the others one. I'd rather have them easy to implement, independently of what the others are.Autosuggestion
S
3

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.

Sofia answered 3/12, 2013 at 15:29 Comment(1)
Doesn't that suffer from each process implementation needing a "Policy next(Policy)" step? That is needed when 'process' rejects and input and resends to 'defaultNext': A new 'defaultNext' must be selected for the send.Advert
B
3

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.

Blouson answered 3/12, 2013 at 16:4 Comment(1)
I tend to agree with you Rob, I don't like the fact a policy has to have knowledge of the others one. I'd rather have them easy to implement, independently of what the others are.Autosuggestion
B
2

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.

Bah answered 22/10, 2018 at 15:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.