Patterns: Chain of Responsibility

The Chain of Responsibility is one of the major patterns I use in my MicroObject projects.

When I have a sequence of events, applying the practices forces things into a very narrow set of forms. I've found one that I feel is the clearest, cleanest, and most inline with the technical practices as well as the non-technical practice I probably haven't clearly defined anywhere...
The big couple I've seen compared to different forms:

  • If your client has to always do things - You do it.
  • A client should interact with an object that does what it wants.

These considerations for how a client consumes the class/module is critical to simpliftying the life of a user. Which sometimes, is ourself.

Chain of Responsibility

Googling about the chain of responsibility I see that none of the top links QUITE represent how I implement this pattern. This is entirely expected for me. :)

A lot of these have an abstract base class to hold the common code of using a setter. Since we don't use setters, we don't need a base class. There's one of them that just has an interface, though still with a setter. No setter for us.

The other big component that these lack; they don't have something that represents what the consumer WANTS to do. I really like the quote from presentations I've seen from Sandi Metz, "I know what I want, you know how to do it". (Googling this for a better source brings up only my posts... sooo...)
The consumers of a chain of responsibility are going to need to know how to construct and order everything.
In our world; that's knowing way too many things. It's entirely unrelated to the behavior they provide to another collaborator. We need to remove that knowledge.

We're engineers and often code is the best explination - It's our picture. :)

We'll use our favorite example; FizzBuzz.

class FizzBuzz : IFizzBuzz {
    string Convert(int value){
        if(input % 15 == 0) return "FizzBuzz";
        if(input % 5 == 0) return "Buzz";
        if(input % 3 == 0) return "Fizz";
        return input.ToString();
    }
}

Here we have a sequence of events. We want to turn this into a chain, we could do this like a lot of the examples from google would have us do;

class FizzBuzz : IFizzBuzz {
    string Convert(int value){
        var fb = new FizzBuzzLink();
        var b = new BuzzLink();
        var f = new FizzLink();
        var i = new ToStringLink();
        
        f.SetNext(i);
        b.SetNext(f);
        fb.SetNext(b)
        
        return fb.Convert();
    }
}

I really hate setters, so let's build them via constuctors like we should.

class FizzBuzz : IFizzBuzz {
    string Convert(int value){
        var actions = new FizzBuzzAction(
            new BuzzAction(
                new FizzAction(
                    new ToStringAction())));
        
        actions.Convert();
    }
}

The biggest issue here is that FizzBuzz news things up in line. This is horrible; let's fix that.

class FizzBuzz : IFizzBuzz {
    private readonly IToString _next;
    public FizzBuzz():this(new FizzBuzzAction(
            new BuzzAction(
                new FizzAction(
                    new ToStringAction())))){};
    prviate FizzBuzz(IToString next) => _next = next;
    
    string Convert(int value){    
        _next.Convert();
    }
}

This is better. No new inline has been achived.

This is almost exactly what I do. The only problem left is that FizzBuzz and our actions implement different interfaces. Let's fix that.
Because I'm looking at having this represent the behavior the consumer wants; the interface needs to represent that. The consumer doesn't want a "ToString" behavior. It's an IFizzBuzz behavior.

class FizzBuzz : IFizzBuzz {
    private readonly IFizzBuzz _next;
    public FizzBuzz():this(new FizzBuzzAction(
            new BuzzAction(
                new FizzAction(
                    new ToStringAction())))){};
    prviate FizzBuzz(IFizzBuzz next) => _next = next;
    
    string Convert(int value){    
        _next.Convert();
    }
}

class Consumer{
    private readonly IFizzBuzz _fizzBuzz;
    void Blah(){
        ...
        _fizzBuzz.Convert(someValue);
    }
}

Now any consumer can use a FizzBuzz and get the behavior.

This is how I construct a chain of responsibility to enable a sequence of events. These can be "chains of chains". If FizzAction decides between two chains and exectures of them before handing off; awesome. We can modify that and nothing else for our consumers changes.

One of the forms I've seen that I think follows a much harder path to break free from is a Builder. The biggest problem is that you're using a builder. This is abandoning the idea of a behavior for the system. A builder doesn't provide behavior. It provides a place to avoid having to new up an object. It follows the law of "no new inline" but violates the spirit. It's a new in the flow of the code. There isn't a collaboration between a builder and the object invoking.
This look something like

class FizzBuzzBuilder : IFizzBuzzBuilder {
    public IFizzBuzz FizzBuzz() => new FizzBuzzAction(
            new BuzzAction(
                new FizzAction(
                    new ToStringAction())));
}

class Consumer{
    private readonly IFizzBuzzBuilder _fizzBuzzBuilder;
    void Blah(){
        ...
        _fizzBuzzBuilder.FizzBuzz().Convert(someValue);
    }
}

This violates the required consideration for our consumer. With the builder we are FORCING them to know about what builds for us. We're FORCING them to know two things; how to get the collaborator AND how to invoke the collaborator... and every consumer will have to do this EVERY time.
This is unacceptable. If a client must always do multiple steps, we do it for them.
We can just encapsulate this relationship into a new object.

class FizzBuzzBuilder : IFizzBuzzBuilder {
    public IFizzBuzz FizzBuzz() => new FizzBuzzAction(
            new BuzzAction(
                new FizzAction(
                    new ToStringAction())));
}
class FizzBuzz : IFizzBuzz {
    private readonly IFizzBuzzBuilder _fizzBuzzBuilder;
    public FizzBuzz():this(new FizzBuzzBuilder()){};
    prviate FizzBuzz(IFizzBuzzBuilder fizzBuzzBuilder) => _fizzBuzzBuilder = fizzBuzzBuilder;
    
    string Convert(int value){   
        _fizzBuzzBuilder.FizzBuzz().Convert(someValue);
    }
}

class Consumer{
    private readonly IFizzBuzz _fizzBuzz;
    void Blah(){
        ...
        _fizzBuzz.Convert(someValue);
    }
}

We save our client consideration... and now we can look at our abstraction layers.

The builder is superficial at this point. We can remove that layer and get back to

class FizzBuzzActions : IFizzBuzz {
    private readonly IFizzBuzz _next;
    public FizzBuzz():this(new FizzBuzzAction(
            new BuzzAction(
                new FizzAction(
                    new ToStringAction())))){};
    prviate FizzBuzz(IFizzBuzz next) => _next = next;
    
    string Convert(int value){    
        _next.Convert();
    }
}

class Consumer{
    private readonly IFizzBuzz _fizzBuzz;
    Consumer():this(new FizzBuzzActions()){}
    Consumer(IFizzBuzz fizzBuzz) => _fizzBuzz = fizzBuzz;
    void Blah(){
        ...
        _fizzBuzz.Convert(someValue);
    }
}

There is a big caveat to this, that I'm not blind to - it requires that the constructor doesn't need a dynamic value.
I haven't seen where we can't avoid needing dynamic values passed into the constructor; they are method arguments instead.

FizzBuzzActions is the concrete with the behavior we need. We use double constructors to maintain loose coupling. IFizzBuzz IS the behavior we want to invoke. The consumer needs to know NOTHING else - Just what has the behavior needed.

This is Object Oriented Programming - Objects Interacting through Behaviors.

Show Comments