프로그래밍 언어
OOP Design Pattern - Behavioral Pattern 1
재호맴매
2024. 10. 14. 22:28
Chain of Responsibility
Chain-of-Responsibility Pattern
- A behavioral design pattern consisting of a source of command objects and a series of processing objects
- 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 exists for adding new processing objects to the end of this chain
- Variation : act as dispatchers, capable of sending commands out in a variety of directions, forming a tree of responsibility
- Can occur recursively, with processing objects calling higher-up processing objects
- Recursion continues until the command is prcessed, or the entire tree has been explored
Overview
- Solves problem like:
- Coupling the sender of a request to its receiver should be avoided
- It should be possible that more than one receiver can handle a request
- Describes how to solve such problems:
- Define a chain of receiver objects having the responsiblity, depending on run-time conditions, to either handle a request or forward it to the next receiver on the chain (if any)
Example
[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
}
// Abstract Handler in chain of responsibility pattern.
public abstract class Logger
{
protected LogLevel logMask;
protected Logger next; // The next handler in the chain
public Logger(LogLevel mask) { this.logMask = mask; }
// Sets the next logger to make a list / chain of handlers.
public Logger SetNext(Logger nextLogger)
{
Logger lastLogger = this;
while (lastLogger.next != null)
{
lastLogger = lastLogger.next;
}
lastLogger.next = nextLogger;
return this;
}
public void Message(string msg, LogLevel severity)
{
// Tru only if any of the logMask bits are set in severity
if ((severity & logMask) != 0) WriteMessage(msg);
if (next != null) next.Message(msg, severity);
}
protected abstract 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);
}
}
public class FileLogger : Logger
{
public FileLogger(LogLevel mask) : base(mask) { }
protected override void WriteMessage(string msg)
{
Console.WriteLine("Writing to log file : " + msg);
}
}
public class Program
{
public static void Main(string[] args)
{
// Build the chain of responsibility
Logger logger = new ConsoleLogger(LogLevel.All)
.SetNext(new EmailLogger(LogLevel.FunctionalMessage | LogLevel.FunctionalError))
.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);
}
}
Command
Command Pattern
- A behavioral design pattern in which an object is used to encapsulate all information needed to perform an action or trigger an event at a later time
- The information includes the method name, the object that owns the method and values for the method parameters
- Command
- Knows about receiver and invokes a method of the receiver
- Values for parameters of the receiver method are stored in the command
- Receiver
- The object to execute methods
- Stored in the command object by aggregation
- Works when the execute() method in command is called
- Invoker
- Knows how to execute a command, and optionally does bookkeeping about the command execution
- Does not know anything about a concrete command
- Knows only about the command interface
- Client : decides which command to execute at which points
Overview
- Solves problem like:
- Coupling the invoker of a request to a particular request should be avoided
- It should be possible to configure an object (that invokes a request) with a request
- Describes how to solve such problem:
- Define separate (command) objects that encapsulate a request
- A class delegates a request to a command object instead of implementing a particular request directly
Example
public interface Command
{
public void Execute();
}
public class CommandFire : Command
{
public void Execute()
{
Fire();
}
public void Fire()
{
Console.WriteLine("Fire");
}
}
public class CommandJump : Command
{
public void Execute()
{
Jump();
}
public void Jump()
{
Console.WriteLine("Jump");
}
}
public class CommandRoll : Command
{
public void Execute()
{
Roll();
}
public void Roll()
{
Console.WriteLine("Roll");
}
}
Interpreter
Interpreter Pattern
- Specifies how to evaluate sentences in a language
- Have a class for each symbol in a specialized computer language
Overview
- Solves problem like:
- A grammar for a simple language should be defined so that sentences in the language can be interpreted
- Describes how to solve such problems:
- Define a grammar for a simple language by defining a Expression class hierarchy and implementing an interpret() operation
- Represent a sentence in the language by an abstract syntax tree (AST) made up of Expression instances
- Interpret a sentence by calling interpret() on the AST
Example
using System;
using System.Collection.Generic;
namespace OOP;
public class Context
{
public Stack<string> Result = new Stack<string>();
}
public interface IExpression
{
public void Interpret(Context context);
}
public abstract class OperatorExpression : IExpression
{
public IExpression Left { set; private get; }
public IExpression Right { set; private get; }
public void Interpret(Context context)
{
Left.Interpret(context);
string leftValue = context.Result.Pop();
Right.Interpret(context);
string rightValue = context.Result.Pop();
DoInterpret(context, leftValue, rightValue);
}
protected abstract void DoInterpret(Context context, string leftValue, string rightValue);
}
public class EqualsExpression : OperatorExpression
{
protected override void DoInterpret(Context context, string leftValue, string rightValue)
{
context.Result.Push(leftValue == rightValue ? "true" : "false");
}
}
public class OrExpression : OperatorExpression
{
protected override void DoInterpret(Context context, string leftValue, string rightValue)
{
context.Result.Push(leftValue == "true" || rightValue == "true" ? "true" : "false");
}
}
public class MyExpression : IExpression
{
public string Value { private get; set; }
public void Interpret(Context context)
{
context.Result.Push(Value);
}
}
public class Program
{
public static void Main()
{
var context = new Context();
var input = new MyExpression();
var expression = new OrExpression
{
Left = new EqualsExpression
{
Left = input,
Right = new MyExpression { Value = "4" }
},
Right = new EqualsExpression
{
Left = input,
Right = new MyExpression { Value = "four" }
}
};
input.Value = "four";
expression.Interpret(context); // Output : "true"
Console.WriteLine(context.Result.Pop());
input.Value = "44";
expression.Interpret(context); // Output : "false"
Console.WriteLine(context.Result.Pop());
}
}