Design patterns in C# – part 16 (Chain of Responsibility pattern)

Role

The Chain of Responsibility pattern works with a list of Handler objects that have limitations on the nature of the requests they can deal with. If an object cannot handle a request, it passes it on the next object in the chain. At the end of the chain, there can be either default or exceptional behavior.

Design

ChainOfResponsibilityPattern

Implementation

  • Client -> TestChainOfResponsibilityPattern
  • IHandler -> IOutputHandler
  • Handler1, Handler2 -> NormalOutputHandler, WarningOutputHandler, ErrorOutputHandler, DebugOutputHandler
using System;

namespace OutputHandler
{
    public enum Output
    {
        Normal,
        Error,
        Warning,
        Debug
    }

    public interface IOutputHandler
    {
        string HandlerKind { get; }
        void Request(Output type, string output);
    }

    public class NormalOutputHandler : IOutputHandler
    {
        public const string OutputText = "{0}: \n\t{1}";
        private readonly IOutputHandler _next;

        public NormalOutputHandler()
        {
            this._next = new WarrningOutputHandler();
        }

        #region IOutputHandler Members

        public string HandlerKind
        {
            get { return Output.Normal.ToString(); }
        }

        public void Request(Output type, string output)
        {
            switch (type)
            {
                case Output.Normal:
                    Console.WriteLine(OutputText, this.HandlerKind, output);
                    break;
                default:
                    this._next.Request(type, output);
                    break;
            }
        }

        #endregion
    }

    internal class ErrorOutputHandler : IOutputHandler
    {
        private readonly IOutputHandler _next;

        internal ErrorOutputHandler()
        {
            this._next = new DebugOutputHandler();
        }

        #region IOutputHandler Members

        public string HandlerKind
        {
            get { return Output.Error.ToString(); }
        }

        public void Request(Output type, string output)
        {
            switch (type)
            {
                case Output.Error:
                    Console.WriteLine(NormalOutputHandler.OutputText, this.HandlerKind, output);
                    break;
                default:
                    this._next.Request(type, output);
                    break;
            }
        }

        #endregion
    }

    internal class WarrningOutputHandler : IOutputHandler
    {
        private readonly IOutputHandler _next;

        internal WarrningOutputHandler()
        {
            this._next = new ErrorOutputHandler();
        }

        #region IOutputHandler Members

        public string HandlerKind
        {
            get { return Output.Warning.ToString(); }
        }

        public void Request(Output type, string output)
        {
            switch (type)
            {
                case Output.Warning:
                    Console.WriteLine(NormalOutputHandler.OutputText, this.HandlerKind, output);
                    break;
                default:
                    this._next.Request(type, output);
                    break;
            }
        }

        #endregion
    }

    internal class DebugOutputHandler : IOutputHandler
    {
        #region IOutputHandler Members

        public string HandlerKind
        {
            get { return Output.Debug.ToString(); }
        }

        public void Request(Output type, string output)
        {
            Console.WriteLine(NormalOutputHandler.OutputText, this.HandlerKind, output);
        }

        #endregion
    }
}
using OutputHandler;

namespace ChainOfResponsibility
{
    public class TestChainOfResponsibilityPattern
    {
        public static void Main(string[] args)
        {
            var outputHandler = new NormalOutputHandler();

            var errorMessage = new MyOutput {Kind = Output.Error, Output = "bad statement ;sa"};
            var warningMessage = new MyOutput {Kind = Output.Warning, Output = "you should not used it"};
            var normalMessage = new MyOutput {Kind = Output.Normal, Output = "myVar = 5"};
            var debugMessage = new MyOutput {Kind = Output.Debug, Output = "it is debug info"};
            var error2Message = new MyOutput {Kind = Output.Error, Output = "very bad stat\nement ;sa"};

            outputHandler.Request(errorMessage.Kind, errorMessage.Output);
            outputHandler.Request(warningMessage.Kind, warningMessage.Output);
            outputHandler.Request(normalMessage.Kind, normalMessage.Output);
            outputHandler.Request(debugMessage.Kind, debugMessage.Output);
            outputHandler.Request(error2Message.Kind, error2Message.Output);
        }

        #region Nested type: MyOutput

        private class MyOutput
        {
            internal Output Kind { get; set; }
            internal string Output { get; set; }
        }

        #endregion
    }
}

OUTPUT:

Error:
bad statement ;sa
Warning:
you should not used it
Normal:
myVar = 5
Debug:
it is debug info
Error:
very bad stat
ement ;sa

Use when

You have:

  • More than one handler for a request
  • Reasons why a handler should pass a request on to another one in the chain
  • A set of handlers that varies dynamically

You want to:

  • Retain flexibility in assigning requests to handlers

Bibliography

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s