11 October 2018

Set of responsibility

So, three and a half years later... I'm back.

According to wikipediathe chain-of-responsibility pattern is a design pattern consisting of a source of command objects and a series of processing objects.[1] Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain.

In some cases, I will benefit from flexibility allowed by this pattern, without being tied to the chain-based structure, e.g. when there is an IoC container involved: Handlers in the pattern all have the same interface, so it's difficult to leave their instantiation to the IoC container.

In such scenario I use a variation of the classic chain of responsibility: there are still responsibilities, off course, but there is no chain out there.

I like to call my variation set of responsibility (or list of responsibility, see above for discussion about this) - the structure is the one that follows (C# code):

interface Handler {
    Result Handle(Request request);
  
    bool CanHandle(Request request);
}


class HandlerSet {
    IEnumerable handlers;

    HandlerSet(IEnumerable < handler > handlers) {
        this.handlers = handlers;
    }
 
    Result Handle(Request request) {
        return this.handlers.Single(h => h.CanHandle(request)).Handle(request);
    }
}

class Sender {
    HandlerSet handler;
 
    Sender(HandlerSet handler) {
        this.handler = handler;
    }

    void FooBar() {
        Request request = ...;
        var result = this.handler.Handle(request);
    }
}

One interesting scenario which I've applied this pattern in is the case in which the Handlers' input type Request hides a hierarchy of different subclasses and each Handler implementation is able to deal with a specific Request subclass: when use polymorphism is not a viable way, e.g. because those classes comes from an external library and are not under our control, we can use set of responsibility in order to cleanup the horrible code that follows:


class RequestX : Request {}

class RequestY : Request {}

class RequestZ : Request {}

class Sender {
    var result = null;
 
    void FooBar() {
        Request request = ...;
        
        if(request is RequestX) {
            result = HandleX((RequestX)request);
        } else if (request is RequestY) {
            result = HandleY((RequestY)request)
        } else if (request is RequestZ) {
            result = HandleZ((RequestZ)request)
        }
    }
}

We can't avoid is and () operators usage, but we can hide them behind a polymorphic interface, adopting a design than conform to open-closed principle:

class Sender {
    HandlerSet handler;
    Sender(HandlerSet handler) {
        this.handler = handler;
    }

    void FooBar() {
  Request request = ...;
     var result = this.handler.Handle(request);
    }
}

class HandlerX : Handler {
    bool CanHandle(Request request) => request is RequestX;
 
    Result Handle(Request request) {
        HandleX((RequestX)request);
    }
}

class HandlerY : Handler {
    bool CanHandle(Request request) => request is RequestY;
 
    Result Handle(Request request) {
        HandleY((RequestY)request);
    }
}

class HandlerZ : Handler {
    bool CanHandle(Request request) => request is RequestZ;
 
    Result Handle(Request request) {
        HandleZ((RequestZ)request);
    }
}

Adding a new Request subclass case is now only a matter of adding a new HandlerAA implemementation of Handler interface, without the need to touch existing code.

I use in cases like the explained one the name of set of responsibility to stress the idea that only one handler of the set can handle a single, specific request (I use _handlers.Single(...) method in HandlerSet implementation, too).

When the order in which the handlers are tested matters, we can adopt a different _handlers.Single(...) strategy: in this case I like to call the pattern variation list of responsibility.

When more than one handler can handle a specific request we can think to variations of this pattern that select all applyable handlers (i.e. those handlers whose CanHandle method returns true for the current request) and apply them to the incoming request.

No comments:

Post a Comment