Pages

Sunday, September 14, 2014

HowTo:: Design patterns with Scala or Java: GoF behavioural pattern Chain of Responsibilities

   
Img.1.: Chain Responsibilities pattern concept

   This pattern is really useful it allows the sender (sends the request) to be decoupled from the receivers. Those receivers are organised into a sequence. 
Inside such sequence every member has a opportunity to process the request or not and forward it to the next one.
  In this blog post I want to compare Java vs. Scala implementations of "Chain of Responsibilities pattern" and its possible implementation (Img.1.).

1. Java 
In following Java implementation is important point  abstract class AbstractHandler1 which has access to the successor property. The successor property provides concrete AbstractHandler1 handler implementation (creates the chain of different handlers).
a) AbstractHandler1:
public abstract  class AbstractHandler1 {
    AbstractHandler1 successor;

    public void setSuccessor(AbstractHandler1 successor){
        this.successor = successor;
    }

    abstract public  void handleRequest(ReqTypeEnum request);
}
Now we create ReqTypeEnum object which is thread-safe and its implementation guarantee that initialisation happens only ones and is immutable. In other hand implementation makes the Java code more readable. 
b) ReqTypeEnum
public enum ReqTypeEnum {
    typeOne(0),
    typeTwo(1),
    typeThree(2);

    private int code;

    private static final Map<Integer, ReqTypeEnum> codeToRequestTypeMapping = new HashMap<Integer, ReqTypeEnum>();
    static{
        for(ReqTypeEnum rt: ReqTypeEnum.values()){
            codeToRequestTypeMapping.put(rt.getCode(), rt);
        }
    }

    private ReqTypeEnum(int c){
        code = c;
    }

    public int getCode(){
        return code;
    }

    public static ReqTypeEnum getRequestType(int code) {
        return codeToRequestTypeMapping.get(code);
    }

}
   After having request handler skeleton (AbstractHandler1defined  and request type (ReqTypeEnum) we can make step forward and create couple of concrete hanlder implementations (example 3):
***************************
public class ConHandler1 extends AbstractHandler1 {
    private static final Logger logger = LoggerFactory.getLogger(ConHandler1.class);
    @Override
    public void handleRequest(ReqTypeEnum request) {
        if(request == ReqTypeEnum.typeOne){
            logger.debug("Concrete Handler 1 = " + request);
        } else {
            logger.debug("Concrete Handler 1 -> Doesn't handle request= " + request);
            if(successor != null){
                successor.handleRequest(request);
            }
        }
    }
}
***************************
public class ConHandler2 extends AbstractHandler1 {

    private static final Logger logger = LoggerFactory.getLogger(ConHandler2.class);

    @Override
    public void handleRequest(ReqTypeEnum request) {
        if(request == ReqTypeEnum.typeTwo){
            logger.debug("Concrete Handler 2 = " + request);
        } else {
            logger.debug("Concrete Handler 2 -> Doesn't handle request= " + request);
            if(successor != null){
                successor.handleRequest(request);
            }
        }
    }
}
***************************
public class ConHandler3 extends AbstractHandler1 {

    private static final Logger logger = LoggerFactory.getLogger(ConHandler3.class);

    @Override
    public void handleRequest(ReqTypeEnum request) {
        if(request == ReqTypeEnum.typeThree){
            logger.debug("Concrete Handler 3 = " + request);
        } else {
            logger.debug("Concrete Handler 3 -> Doesn't handle request= " + request);
            if(successor != null){
                successor.handleRequest(request);
            }
        }
    }
}
and prepare demo setup:
private static AbstractHandler1 setDemoUp(){
   ConHandler1 conHandler1 = new ConHandler1();
   ConHandler2 conHandler2 = new ConHandler2();
   ConHandler3 conHandler3 = new ConHandler3();

   conHandler1.setSuccessor(conHandler2);
   conHandler2.setSuccessor(conHandler3);

   return conHandler1;
}
  Java implementation is ready to launch  
public static void main(String... args){
   ...
   AbstractHandler1 responsibilityChain = setDemoUp();

   responsibilityChain.handleRequest(ReqTypeEnum.typeOne);
   responsibilityChain.handleRequest(ReqTypeEnum.typeTwo);
   responsibilityChain.handleRequest(ReqTypeEnum.typeThree);
   ...
}
  The demo output shows the chain of request handling:
ChainDemo1 - ***Start Chain of Responsibility Demo***
ConHandler1 - Concrete Handler 1 = typeOne
ConHandler1 - Concrete Handler 1 -> Doesn't handle request= typeTwo
ConHandler2 - Concrete Handler 2 = typeTwo
ConHandler1 - Concrete Handler 1 -> Doesn't handle request= typeThree
ConHandler2 - Concrete Handler 2 -> Doesn't handle request= typeThree
ConHandler3 - Concrete Handler 3 = typeThree


2.Scala
  As Java is up, let's move to the Scala possible implementation. This implementation should allow runtime configuration of registered handlers in the chain. Each handler is implemented as Scala object:
object ConHandler1 extends ChainHandler[String]{
  private val name: String = "Concrete Handler One"
  private val logger = LoggerFactory.getLogger(getClass)
  def accept(obj: String) = obj equals name
  def handle(obj: String) = logger.debug("SERVUS " + name)
}
***************************
object ConHandler2 extends ChainHandler[String]{
  private val name: String = "Concrete Handler Two"
  private val logger = LoggerFactory.getLogger(getClass)
  def accept(obj: String) = obj equals name
  def handle(obj: String) = logger.debug("HALLO " + name)
} 
  The ChainHandler is implemented as the generic trait and indicates that every concrete handler implementation, inside the chain, is able to decide whether is able to handle the request.
trait ChainHandler[T] {
  def accept(obj: T): Boolean
  def handle(obj: T): Unit
}
Each request is passed as the parameter which concrete handler uses for processing.
  The process of the requests chain is controlled by ChainManager Scala class. ChainManager groups all assigned concrete handlers and put them into the ListBuffer (chain). 
case class ChainManager[T](n: String) {
  private val logger = LoggerFactory.getLogger(getClass)
  val name: String = n
  private val chain = new ListBuffer[ChainHandler[T]]
  def add(h: ChainHandler[T]) = {
    chain += h
    ChainManager.this
  }
  def apply(obj: T) = {
    val handler = Option(chain.filter(n => n accept obj) head)
    handler match {
      case Some(chainElement) => chainElement handle obj
      case None => logger.error("no handler")
    }
  }
}
As the add() handler methods returns references to the ChainManager itself. It allows linking add() method to simplify the construction.  
controller.add(ConHandler1).add(ConHandler2)
Now we have all elements to run the sample program: 
object ChainOfRespManagerTest {
  private val logger = LoggerFactory.getLogger(getClass)
  def main(args: Array[String]): Unit = {
    val controller = ChainManager[String]("Manager Approach")
    logger.debug("---Chain of Responsibility: "+ controller.name +"---")
    controller.add(ConHandler1).add(ConHandler2)
    controller apply "Concrete Handler One"
    controller apply "Concrete Handler Two"
  }
}
and observe the out put of it:
ChainOfRespManagerTest$ - ---Chain of Responsibility: Manager Approach---
ConHandler1$ - SERVUS Concrete Handler One
ConHandler2$ - HALLO Concrete Handler Two
  

  Conclusion: Although java is cool language such implementation shows necessity to write not only little more code to achieve the similar result but also the solution is not, in some parts, generics enough. 
   The Scala solution illustrates how the invocation of the chain behaviour can be made to look like the part of the language product.   
Enjoy ! 

No comments: