Chain of responsibility: loop or next?

1.9k views Asked by At

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?

3

There are 3 answers

1
Rob On BEST ANSWER

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.

1
OldCurmudgeon On

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.

0
jaco0646 On

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.