Chain-of-responsibility pattern

From Wikipedia, the free encyclopedia

In Object Oriented Design, the chain-of-responsibility pattern is a design pattern consisting of a source of command objects and a series of processing objects. Each processing object contains a set of logic that describes the types of command objects that it can handle, and how to pass off those that it cannot to the next processing object in the chain. A mechanism also exists for adding new processing objects to the end of this chain.

In a variation of the standard chain-of-responsibility model, some handlers may act as dispatchers, capable of sending commands out in a variety of directions, forming a tree of responsibility. In some cases, this can occur recursively, with processing objects calling higher-up processing objects with commands that attempt to solve some smaller part of the problem; in this case recursion continues until the command is processed, or the entire tree has been explored. An XML interpreter (parsed, but not yet executed) might be a fitting example.

This pattern promotes the idea of loose coupling, which is considered a programming best practice.

Contents

[edit] Examples

[edit] Java

The following Java code illustrates the pattern with the example of a logging class. Each logging handler decides if any action is to be taken at this log level and then passes the message on to the next logging handler. The output is:

  Writing to debug output: Entering function y.
  Writing to debug output: Step1 completed.
  Sending via e-mail:      Step1 completed.
  Writing to debug output: An error has occurred.
  Sending via e-mail:      An error has occurred.
  Writing to stderr:       An error has occurred.

Note that this example should not be seen as a recommendation to write Logging classes this way.

Also, note that in a 'pure' implementation of the CoR a logger would not pass responsibility further down the chain after handling a message. In this example a message will be passed down the chain whether it is handled or not.

import java.util.*;
 
abstract class Logger 
{
    public static int ERR = 3;
    public static int NOTICE = 5;
    public static int DEBUG = 7;
    protected int mask;
 
    // The next element in the chain of responsibility
    protected Logger next;
    public Logger setNext( Logger l)
    {
        next = l;
        return this;
    }
 
    public void message( String msg, int priority )
    {
        if ( priority <= mask ) 
        {
            writeMessage( msg );
            if ( next != null )
            {
                next.message( msg, priority );
            }
        }
    }
 
    abstract protected void writeMessage( String msg );
 
}
 
class StdoutLogger extends Logger 
{
 
    public StdoutLogger( int mask ) { this.mask = mask; }
 
    protected void writeMessage( String msg )
    {
        System.out.println( "Writing to stdout: " + msg );
    }
}
 
 
class EmailLogger extends Logger 
{
 
    public EmailLogger( int mask ) { this.mask = mask; }
 
    protected void writeMessage( String msg )
    {
        System.out.println( "Sending via email: " + msg );
    }
}
 
class StderrLogger extends Logger 
{
 
    public StderrLogger( int mask ) { this.mask = mask; }
 
    protected void writeMessage( String msg )
    {
        System.out.println( "Sending to stderr: " + msg );
    }
}
 
public class ChainOfResponsibilityExample
{
    public static void main( String[] args )
    {
        // Build the chain of responsibility
        Logger l = new StdoutLogger( Logger.DEBUG).setNext(
                            new EmailLogger( Logger.NOTICE ).setNext(
                            new StderrLogger( Logger.ERR ) ) );
 
        // Handled by StdoutLogger
        l.message( "Entering function y.", Logger.DEBUG );
 
        // Handled by StdoutLogger and EmailLogger
        l.message( "Step1 completed.", Logger.NOTICE );
 
        // Handled by all three loggers
        l.message( "An error has occurred.", Logger.ERR );
    }
}

[edit] C#, Abstract class based implementation

using System;
 
namespace Chain_of_responsibility
{
        public abstract class Chain
        {
                private Chain _next;
 
                public Chain Next
                {
                        get
                        {
                                return _next;
                        }
                        set
                        {
                                _next = value;
                        }
                }
 
                public void Message(object command)
                {
                        if ( Process(command) == false && _next != null )
                        {
                                _next.Message(command);
                        }
                }
 
                public static Chain operator +(Chain lhs, Chain rhs)
                {
                        Chain last = lhs;
 
                        while ( last.Next != null )
                        {
                                last = last.Next;
                        }
 
                        last.Next = rhs;
 
                        return lhs;
                }
 
                protected abstract bool Process(object command);
        }
 
        public class StringHandler : Chain
        {
                protected override bool Process(object command)
                {
                        if ( command is string )
                        {
                                Console.WriteLine("StringHandler can handle this message : {0}",(string)command);
 
                                return true;
                        }
 
                        return false;
                }
        }
 
        public class IntegerHandler : Chain
        {
                protected override bool Process(object command)
                {
                        if ( command is int )
                        {
                                Console.WriteLine("IntegerHandler can handle this message : {0}",(int)command);
 
                                return true;
                        }
 
                        return false;
                }
        }
 
        public class NullHandler : Chain
        {
                protected override bool Process(object command)
                {
                        if ( command == null )
                        {
                                Console.WriteLine("NullHandler can handle this message.");
 
                                return true;
                        }
 
                        return false;
                }
        }
 
        public class IntegerBypassHandler : Chain
        {
                protected override bool Process(object command)
                {
                        if ( command is int )
                        {
                                Console.WriteLine("IntegerBypassHandler can handle this message : {0}",(int)command);
 
                                return false; // Always pass to next handler
                        }
 
                        return false; // Always pass to next handler
                }
        }
 
        class TestMain
        {
                static void Main(string[] args)
                {
                        Chain chain = new StringHandler();
                        chain += new IntegerBypassHandler();
                        chain += new IntegerHandler();
                        chain += new IntegerHandler(); // Never reached
                        chain += new NullHandler();
 
                        chain.Message("1st string value");
                        chain.Message(100);
                        chain.Message("2nd string value");
                        chain.Message(4.7f); // not handled
                        chain.Message(null);
                }
        }
}

[edit] Interface based implementation

using System;
using System.Collections;
 
namespace Chain_of_responsibility
{
        public interface IChain
        {
                bool Process(object command);
        }
 
        public class Chain
        {
                private ArrayList _list;
 
                public ArrayList List
                {
                        get
                        {
                                return _list;
                        }
                }
 
                public Chain()
                {
                        _list = new ArrayList();
                }
 
                public void Message(object command)
                {
                        foreach ( IChain item in _list )
                        {
                                bool result = item.Process(command);
 
                                if ( result == true ) break;
                        }
                }
 
                public void Add(IChain handler)
                {
                        List.Add(handler);
                }
        }
 
        public class StringHandler : IChain
        {
                public bool Process(object command)
                {
                        if ( command is string )
                        {
                                Console.WriteLine("StringHandler can handle this message : {0}",(string)command);
 
                                return true;
                        }
 
                        return false;
                }
        }
 
        public class IntegerHandler : IChain
        {
                public bool Process(object command)
                {
                        if ( command is int )
                        {
                                Console.WriteLine("IntegerHandler can handle this message : {0}",(int)command);
 
                                return true;
                        }
 
                        return false;
                }
        }
 
        public class NullHandler : IChain
        {
                public bool Process(object command)
                {
                        if ( command == null )
                        {
                                Console.WriteLine("NullHandler can handle this message.");
 
                                return true;
                        }
 
                        return false;
                }
        }
 
        public class IntegerBypassHandler : IChain
        {
                public bool Process(object command)
                {
                        if ( command is int )
                        {
                                Console.WriteLine("IntegerBypassHandler can handle this message : {0}",(int)command);
 
                                return false; // Always pass to next handler
                        }
 
                        return false; // Always pass to next handler
                }
        }
 
        class TestMain
        {
                static void Main(string[] args)
                {
                        Chain chain = new Chain();
 
                        chain.Add(new StringHandler());
                        chain.Add(new IntegerBypassHandler());
                        chain.Add(new IntegerHandler());
                        chain.Add(new IntegerHandler()); // Never reached
                        chain.Add(new NullHandler());
 
                        chain.Message("1st string value");
                        chain.Message(100);
                        chain.Message("2nd string value");
                        chain.Message(4.7f); // not handled
                        chain.Message(null);
                }
        }
}

[edit] Delegate based implementation

using System;
 
namespace Chain_of_responsibility
{
        public class StringHandler
        {
                private static int count;
 
                public void Process(object command)
                {
                        if ( command is string )
                        {
                                string th;
                                count++;
 
                                if ( count % 10 == 1 ) th = "st";
                                else if ( count % 10 == 2 ) th = "nd";
                                else if ( count % 10 == 3 ) th = "rd";
                                else th = "th";
 
                                Console.WriteLine("StringHandler can handle this {0}{1} message : {2}",count,th,(string)command);
                        }
                }
        }
 
        public class StringHandler2
        {
                public void Process(object command)
                {
                        if ( command is string )
                        {
                                Console.WriteLine("StringHandler2 can handle this message : {0}",(string)command);
                        }
                }
        }
 
        public class IntegerHandler
        {
                public void Process(object command)
                {
                        if ( command is int )
                        {
                                Console.WriteLine("IntegerHandler can handle this message : {0}",(int)command);
                        }
                }
        }
 
        class Chain
        {
                public delegate void MessageHandler(object message);
                public static event MessageHandler Message;
 
                public static void Process(object message)
                {
                        Message(message);
                }
        }
 
        class TestMain
        {
                static void Main(string[] args)
                {
                        StringHandler sh1 = new StringHandler();
                        StringHandler2 sh2 = new StringHandler2();
                        IntegerHandler ih = new IntegerHandler();
 
                        Chain.Message += new Chain.MessageHandler(sh1.Process);
                        Chain.Message += new Chain.MessageHandler(sh2.Process);
                        Chain.Message += new Chain.MessageHandler(ih.Process);
                        Chain.Message += new Chain.MessageHandler(ih.Process); // Can also be reached
 
                        Chain.Process("1st string value");
                        Chain.Process(100);
                        Chain.Process("2nd string value");
                        Chain.Process(4.7f); // not handled
                }
        }
}

[edit] PHP

<?php
abstract class Logger {
        const ERR = 3;
        const NOTICE = 5;
        const DEBUG = 7;
 
        protected $mask;
        protected $next; // The next element in the chain of responsibility
 
        public function setNext(Logger $l) {
                $this->next = $l;
                return $this;
        }
 
        abstract public function message($msg, $priority);
}
 
class DebugLogger extends Logger {
        public function __construct($mask) {
                $this->mask = $mask;
        }
 
        public function message($msg, $priority) {
                if ($priority <= $this->mask) {
                        echo "Writing to debug output: {$msg}\n";
                }
 
                if (false == is_null($this->next)) {
                        $this->next->message($msg, $priority);
                }
        }
}
 
class EmailLogger extends Logger {
        public function __construct($mask) {
                $this->mask = $mask;
        }
 
        public function message($msg, $priority) {
                if ($priority <= $this->mask) {
                        echo "Sending via email: {$msg}\n";
                }
 
                if (false == is_null($this->next)) {
                        $this->next->message($msg, $priority);
                }
        }
}
 
class StderrLogger extends Logger {
        public function __construct($mask) {
                $this->mask = $mask;
        }
 
        public function message($msg, $priority) {
                if ($priority <= $this->mask) {
                        echo "Writing to stderr: {$msg}\n";
                }
 
                if (false == is_null($this->next)) {
                        $this->next->message($msg, $priority);
                }
        }
}
 
class ChainOfResponsibilityExample {
        public function __construct() {
                // build the chain of responsibility
                $l = new DebugLogger(Logger::DEBUG);
                $e = new EmailLogger(Logger::NOTICE);
                $s = new StderrLogger(Logger::ERR);
 
                $e->setNext($s);
                $l->setNext($e);
 
                $l->message("Entering function y.",                Logger::DEBUG);           // handled by DebugLogger
                $l->message("Step1 completed.",                    Logger::NOTICE);  // handled by DebugLogger and EmailLogger
                $l->message("An error has occurred.",      Logger::ERR);             // handled by all three Loggers
        }
}
 
new ChainOfResponsibilityExample();
?>

[edit] Visual Prolog

This is a realistic example of protecting the operations on a stream with a named mutex.

First the outputStream interface (a simplified version of the real one):

interface outputStream
   predicates
      write : (...).
      writef : (string FormatString, ...).
end interface outputStream

This class encapsulates each of the stream operations the mutex. The finally predicate is used to ensure that the mutex is released no matter how the operation goes (i.e. also in case of exceptions). The underlying mutex object is released by a finalizer in the mutex class, and for this example we leave it at that.

class outputStream_protected : outputStream
   constructors
      new : (string MutexName, outputStream Stream).
end class outputStream_protected

#include @"pfc\multiThread\multiThread.ph"

implement outputStream_protected
   facts
      mutex : mutex.
      stream : outputStream.
   clauses
      new(MutexName, Stream) :-
         mutex := mutex::createNamed(MutexName, false), % do not take ownership
         stream := Stream.
   clauses
      write(...) :-
         _ = mutex:wait(),  % ignore wait code in this simplified example
         finally(stream:write(...), mutex:release()).

   clauses
      writef(FormatString, ...) :-
         _ = mutex:wait(),  % ignore wait code in this simplified example
         finally(stream:writef(FormatString, ...), mutex:release()).
end implement outputStream_protected

Usage example.

The client uses an outputStream. Instead of receiving the pipeStream directly, it gets the protected version of it.

#include @"pfc\pipe\pipe.ph"

goal
   Stream = pipeStream::openClient("TestPipe"),
   Protected = outputStream_protected::new("PipeMutex", Stream),
   client(Protected),
   Stream:close().


[edit] ColdFusion

<cfcomponent name="AbstractLogger" hint="Base Logging Class">
 
        <cfset this['ERROR'] = 3 />
        <cfset this['NOTICE'] = 5 />
        <cfset this['DEBUG'] = 7 />
        <cfset mask = 0 />
 
        <cffunction name="init" access="public" returntype="AbstractLogger" hint="Constructor.">
                <cfreturn this />
        </cffunction>
 
        <cffunction name="setMask" access="public" returntype="AbstractLogger" output="false" hint="I set the mask value.">
                <cfargument name="maskName" type="string" required="true" />
                <cfset mask = this[arguments.maskName] />
                <cfreturn this />
        </cffunction>
 
        <cffunction name="setNext" access="public" returntype="AbstractLogger" output="false" hint="I set the next Logger in the chain">
                <cfargument name="logger" type="AbstractLogger" required="true" />
                <cfset next = arguments.logger />
                <cfreturn this />
        </cffunction>
 
        <cffunction name="message" access="public" returntype="void" output="true" hint="I generate the Log Message.">
                <cfargument name="msg" type="string" required="true" />
                <cfargument name="priority" type="numeric" required="true" />
                <cfset var local = StructNew() />
                <cfif arguments.priority lte mask>
                        <cfset writeMessage(arguments.msg) />
                        <cfif StructKeyExists(variables, 'next')>
                                <cfset next.message(arguments.msg, arguments.priority) />
                        </cfif>
                </cfif>
        </cffunction>
 
</cfcomponent>
 
<cfcomponent name="DebugLogger" extends="AbstractLogger" hint="Standard Logger">
 
        <cffunction name="init" access="public" returntype="DebugLogger" hint="Constructor.">
                <cfreturn this />
        </cffunction>
 
        <cffunction name="writeMessage" access="public" returntype="void" output="true" hint="Generate the message.">
                <cfargument name="msg" type="string" required="true" />
                <cfoutput>Writing to debug: #arguments.msg#</cfoutput>
        </cffunction>
 
</cfcomponent>
 
<cfcomponent name="EmailLogger" extends="AbstractLogger" hint="Email Logger">
 
        <cffunction name="init" access="public" returntype="EmailLogger" hint="Constructor.">
                <cfreturn this />
        </cffunction>
 
        <cffunction name="writeMessage" access="public" returntype="void" output="true" hint="Generate the message.">
                <cfargument name="msg" type="string" required="true" />
                <cfoutput>Sending via email: #arguments.msg#</cfoutput>
        </cffunction>
 
</cfcomponent>
 
<cfcomponent name="ErrorLogger" extends="AbstractLogger" hint="Error Logger">
 
        <cffunction name="init" access="public" returntype="ErrorLogger" hint="Constructor.">
                <cfreturn this />
        </cffunction>
 
        <cffunction name="writeMessage" access="public" returntype="void" output="true" hint="Generate the message.">
                <cfargument name="msg" type="string" required="true" />
                <cfoutput>Sending to error: #arguments.msg#</cfoutput>
        </cffunction>
 
</cfcomponent>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 
<html>
<head>
<title>ColdFusion Example of Chain of Responsibility Pattern</title>
</head>
 
<body>
<h1>ColdFusion Example of Chain of Responsibility Pattern</h1>
 
<cfset logger = CreateObject('component', 'DebugLogger').init().setMask('DEBUG') />
<cfset emailLogger = CreateObject('component', 'EmailLogger').init().setMask('NOTICE') />
<cfset errorLogger = CreateObject('component', 'ErrorLogger').init().setMask('ERROR') />
<cfset logger.setNext(emailLogger.setNext(errorLogger)) />
 
<!--- Handled by StdoutLogger --->
<cfset logger.message("Entering function y.", logger['DEBUG']) /><br /><br />
<!--- Handled by StdoutLogger and EmailLogger --->
<cfset logger.message("Step1 completed.", logger['NOTICE']) /><br /><br />
<!--- Handled by all three loggers --->
<cfset logger.message("An error has occurred.", logger['ERROR']) /><br /><br />
 
</body>
</html>

[edit] See also

[edit] External links