Chain of Responsibility Design Pattern

0

The chain of responsibility pattern as the name suggests allows the responsibility to be shared among a chain of objects where each object knows what to handle and what not.

In our example, we will show how a message is transformed by multiple processors where each processor transforms one specific aspect.

  1. Adds a prefix to the message
  2. Transforms the string to its upper case
  3. Adds an attribute
  4. Logs more details about the message based on whether the mode is development or production
  5. Executes all of the above in asynchronous way

If it decides not to handle the request, it will delegate the responsibility to the next chain of handler. This will go on with each handler till the end of chain is reached.

 

Chain of Responsibility Design Pattern

Chain of Responsibility Design Pattern

In our message processing example, MessageProcessorChain knows the chain of message processors involved in transforming the message. As you can see below, a message processor chain itself is a message processor, Async is a MessageProcessor as well as a MessageProcessorChain.

Below interface deals with a single message processor. It consumes an event and returns the processed event.

MessageProcessor:

package com.javarticles.patterns;

public interface MessageProcessor {
    MyEvent process(MyEvent myEvent); 
}

Message processor chain represents a string of message processors which will process the event turn by turn.

MessageProcessorChain:

package com.javarticles.patterns;

import java.util.List;

public interface MessageProcessorChain extends MessageProcessor {
    void startProcessing();
    MessageProcessorChain addMessageProcessor(MessageProcessor processor);
    List<MessageProcessor> getMessageProcessors();
    void doneProcessing();
}

The message processed is an event object which contains a content and some attributes.

MyEvent:

package com.javarticles.patterns;

import java.util.HashMap;
import java.util.Map;

public class MyEvent {
    private String content;
    private Map<String, Object> attributes = new HashMap<>();
    
    public MyEvent(String content) {
        this.content = content;
    }
    
    public MyEvent(MyEvent myEvent, String content) {
        this.content = content;
        attributes = new HashMap<>(myEvent.getAttributes());
    }

    private Map<String, Object> getAttributes() {
        return attributes;
    }

    MyEvent addAttribute(String key, Object value) {
        attributes.put(key, value);
        return this;
    }
    
    public String getContent() {
        return content;
    }
    
    public Object getAttribute(String key) {
        return attributes.get(key);
    }

    public String toString() {
        return "MyEvent[attributes(" + this.attributes + ")-content(" + this.content + ")]";
    }
}

Let’s look into the chain of processors that we are using.

First one adds a prefix to the content.

PrefixAppender:

package com.javarticles.patterns;

public class PrefixAppender implements MessageProcessor {
    private String prefix;
    
    public PrefixAppender(String prefix) {
        this.prefix = prefix;
    }

    @Override
    public MyEvent process(MyEvent myEvent) {
        return new MyEvent(myEvent, prefix + myEvent.getContent());
    }

}

Next one Capitalizes the string.

StringCapitalize:

package com.javarticles.patterns;

public class StringCapitalize implements MessageProcessor {

    @Override
    public MyEvent process(MyEvent myEvent) {
        return new MyEvent(myEvent, myEvent.getContent().toUpperCase());
    }
}

We may also want to add attributes in key, value form to tweak behaviour. For example, skip certain things when you are in “production” mode.

AddAttribute:

package com.javarticles.patterns;

public class AddAttribute implements MessageProcessor {
    private String key;
    private String value;
    
    public AddAttribute(String key, String value) {
        this.key = key;
        this.value = value;
    }

    @Override
    public MyEvent process(MyEvent myEvent) {
        myEvent.addAttribute(key, value);
        return myEvent;
    }
}

Logs the event object. In case the mode is “dev”, we also log the current thread name.

Logging:

package com.javarticles.patterns;

public class Logging implements MessageProcessor {

    @Override
    public MyEvent process(MyEvent myEvent) {
        if ((Boolean)myEvent.getAttribute("dev")) {
            System.out.println("We are in dev mode..." + Thread.currentThread().getName() + " Logging " + myEvent);
        } else {
            System.out.println("We are in prod mode... Logging " + myEvent);
        }
        return myEvent;
    }

} 

Processor that processes the entire chain in a separate thread.

Async:

package com.javarticles.patterns;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Async extends MessageProcessorChainImpl {
    
    public Async(MessageProcessor...messageProcessors) {
        super(messageProcessors);
    }

    @Override
    public MyEvent process(MyEvent myEvent) {
        executorService.submit(new Runnable() {            
            @Override
            public void run() {
                Async.super.process(myEvent);
            }
        });
        return myEvent;
    }
    
    @Override
    public void doneProcessing() {
        executorService.shutdown();
    }

    private ExecutorService executorService = Executors.newSingleThreadExecutor();
}

Here is the complete example where we first build our message processors chain and then process it. We do it first for “dev” mode and then “prod”.

ChainOfResponsibilitiesDesignPatternExample:

package com.javarticles.patterns;

public class ChainOfResponsibilitiesDesignPatternExample {
    public static void main(String[] args) {
        MyEvent myEvent = new MyEvent("Joe");
        MessageProcessorChain chain = new MessageProcessorChainImpl();
        chain.addMessageProcessor(new PrefixAppender("Hello Mr. "))
        .addMessageProcessor(new StringCapitalize())
        .addMessageProcessor(new AddAttribute("dev", Boolean.TRUE))
        .addMessageProcessor(new Logging());
        
        MessageProcessorChain ayncChain = new Async(chain);
        ayncChain.process(myEvent);
        
        chain = new MessageProcessorChainImpl();
        chain.addMessageProcessor(new PrefixAppender("Hello Mr. "))
        .addMessageProcessor(new StringCapitalize())
        .addMessageProcessor(new AddAttribute("dev", Boolean.FALSE))
        .addMessageProcessor(new Logging());
        
        ayncChain = new Async(chain);   
        ayncChain.process(myEvent);
    }
}

Output:

We are in prod mode... Logging MyEvent[attributes({dev=false})-content(HELLO MR. JOE)]
We are in dev mode...pool-1-thread-1 Logging MyEvent[attributes({dev=true})-content(HELLO MR. JOE)]

Download the source code

This was an example about chain of responsibilities design pattern.

You can download the source code here: designPatternChainOfResposibilities.zip
Share.

Comments are closed.