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.[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. 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 might work in this manner.
This pattern promotes the idea of loose coupling, which is considered a programming best practice.
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 might work in this manner.
This pattern promotes the idea of loose coupling, which is considered a programming best practice.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
namespace killercodes.designPatterns.ChainOfResponsibility | |
{ | |
[Flags] | |
public enum LogLevel | |
{ | |
None = 0, // 0 | |
Info = 1, // 1 | |
Debug = 2, // 10 | |
Warning = 4, // 100 | |
Error = 8, // 1000 | |
FunctionalMessage = 16, // 10000 | |
FunctionalError = 32, // 100000 | |
All = 63 // 111111 | |
} | |
/// <summary> | |
/// Abstract Handler in chain of responsibility pattern. | |
/// </summary> | |
public abstract class Logger | |
{ | |
protected LogLevel logMask; | |
// The next Handler in the chain | |
protected Logger next; | |
public Logger(LogLevel mask) | |
{ | |
this.logMask = mask; | |
} | |
/// <summary> | |
/// Sets the Next logger to make a list/chain of Handlers. | |
/// </summary> | |
public Logger SetNext(Logger nextlogger) | |
{ | |
next = nextlogger; | |
return nextlogger; | |
} | |
public void Message(string msg, LogLevel severity) | |
{ | |
if ((severity & logMask) != 0) //True only if all logMask bits are set in severity | |
{ | |
WriteMessage(msg); | |
} | |
if (next != null) | |
{ | |
next.Message(msg, severity); | |
} | |
} | |
abstract protected void WriteMessage(string msg); | |
} | |
public class ConsoleLogger : Logger | |
{ | |
public ConsoleLogger(LogLevel mask) | |
: base(mask) | |
{ } | |
protected override void WriteMessage(string msg) | |
{ | |
Console.WriteLine("Writing to console: " + msg); | |
} | |
} | |
public class EmailLogger : Logger | |
{ | |
public EmailLogger(LogLevel mask) | |
: base(mask) | |
{ } | |
protected override void WriteMessage(string msg) | |
{ | |
// Placeholder for mail send logic, usually the email configurations are saved in config file. | |
Console.WriteLine("Sending via email: " + msg); | |
} | |
} | |
class FileLogger : Logger | |
{ | |
public FileLogger(LogLevel mask) | |
: base(mask) | |
{ } | |
protected override void WriteMessage(string msg) | |
{ | |
// Placeholder for File writing logic | |
Console.WriteLine("Writing to Log File: " + msg); | |
} | |
} | |
public class Program | |
{ | |
public static void Main(string[] args) | |
{ | |
// Build the chain of responsibility | |
Logger logger, logger1, logger2; | |
logger = new ConsoleLogger(LogLevel.All); | |
logger1 = logger.SetNext(new EmailLogger(LogLevel.FunctionalMessage | LogLevel.FunctionalError)); | |
logger2 = logger1.SetNext(new FileLogger(LogLevel.Warning | LogLevel.Error)); | |
// Handled by ConsoleLogger since the console has a loglevel of all | |
logger.Message("Entering function ProcessOrder().", LogLevel.Debug); | |
logger.Message("Order record retrieved.", LogLevel.Info); | |
// Handled by ConsoleLogger and FileLogger since filelogger implements Warning & Error | |
logger.Message("Customer Address details missing in Branch DataBase.", LogLevel.Warning); | |
logger.Message("Customer Address details missing in Organization DataBase.", LogLevel.Error); | |
// Handled by ConsoleLogger and EmailLogger as it implements functional error | |
logger.Message("Unable to Process Order ORD1 Dated D1 For Customer C1.", LogLevel.FunctionalError); | |
// Handled by ConsoleLogger and EmailLogger | |
logger.Message("Order Dispatched.", LogLevel.FunctionalMessage); | |
} | |
} | |
} | |
/* Output | |
Writing to console: Entering function ProcessOrder(). | |
Writing to console: Order record retrieved. | |
Writing to console: Customer Address details missing in Branch DataBase. | |
Writing to Log File: Customer Address details missing in Branch DataBase. | |
Writing to console: Customer Address details missing in Organization DataBase. | |
Writing to Log File: Customer Address details missing in Organization DataBase. | |
Writing to console: Unable to Process Order ORD1 Dated D1 For Customer C1. | |
Sending via email: Unable to Process Order ORD1 Dated D1 For Customer C1. | |
Writing to console: Order Dispatched. | |
Sending via email: Order Dispatched. | |
*/ |