프로그래밍 언어

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)

Class UML of Chain-of-Responsibility pattern

 

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

Class UML of command pattern

 

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

Class UML of interpreter pattern

 

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());
    }
}