Software Design Patterns

Florian Rappl, Fakultät für Physik, Universität Regensburg

Software Design Patterns

Introduction to modern software architecture

software architecture

Behavioral patterns

Introduction

  • Deal with communication between objects
  • Try to identify common communication
  • Intentions for implementing such patterns:
    1. Increased flexibility
    2. Determined program flow
    3. Optimize resource usage
  • Sometimes patterns introduced here are already included in languages

The observer pattern

  • How to notify an unknown amount of unknown objects with a specific message?
  • The solution to this question is the observer pattern
  • An object, called subject, is maintaining a list of dependents
  • They are called observers and contain a known method
  • This method is invoked once the state changes
  • The invocation is usually called firing, with the message being an event

Observer diagram

skinparam classAttributeIconSize 0
interface Observer {
  {abstract} +notify(): void
}
class ConcreteObserverA extends Observer {
  +notify(): void
}
class ConcreteObserverB extends Observer {
  +notify(): void
}
class Subject {
  -observerCollection
  #notify(): void
  +attach(observer): void
  +detach(observer): void
}
note bottom: notify()\n for observer in observerCollection\n\tcall observer.notify()
Subject o-> Observer

Remarks

  • Key part of MVC (Model View Controller) architecture (introduced later)
  • First implemented in Smalltalk, which introduced MVC and OOP in general and was used to create the first OS with a GUI
  • Almost all GUI frameworks contain this pattern
  • C# has it inbuilt using the event keyword
  • In Java java.util.Observable contains Observable
  • This pattern enables de-coupled messaging

Anonymous mute observer

public class Subject
{
    private List<IObserver> _observers;
    private string _state;

    public Subject() 
    {
        _observers = new List<IObserver>();
    }

    public string State
    {
        get { return _state; }
        set 
        {
            _state = value;
            Notify();
        }
    }

    public void Attach(IObserver o)
    {
        _observers.Add(o);
    }

    public void Detach(IObserver o)
    {
        _observers.Remove(o);
    }

    protected void Notify()
    {
        foreach (IObserver o in _observers)
        {
            o.Update(this);
        }
    }
}
public interface IObserver
{
    void Update(object sender);
}
public class ConcreteObserver : IObserver
{
    public void Update(object sender)
    {
        /* Do something with the sender */
    }
}
public class Subject {
    private List<Observer> _observers;
    private string _state;

    public Subject() {
        _observers = new List<Observer>();
    }

    public string getState() {
        return _state;
    }
    
    public void setState(string value) {
        _state = value;
        Notify();
    }

    public void attach(Observer o) {
        _observers.add(o);
    }

    public void detach(Observer o) {
        _observers.remove(o);
    }

    protected void notify() {
        for (Observer o : _observers) {
            o.update(this);
        }
    }
}
public interface Observer {
    void update(object sender);
}
public class ConcreteObserver implements Observer {
    public void update(object sender) {
        /* Do something with the sender */
    }
}
class Object {
};
class Subject : public Object {
    private:
    list<Observer*>* _observers;
    string _state;

    public:
    Subject() {
        _observers = new list<Observer*>();
    }

    string GetState() {
        return _state;
    }
    
    void SetState(string value) {
        _state = value;
        Notify();
    }

    void Attach(Observer* o) {
        _observers->push_back(o);
    }

    void Detach(Observer* o) {
        _observers->remove(o);
    }

    protected:
    void Notify() {
        for (list<Observer*>::iterator o = _observers->begin(); o != _observers->end(); ++o) {
            (*o)->Update(this);
        }
    }
};
class Observer {
    public:
    virtual void Update(Object* sender) = 0;
};
class ConcreteObserver : public Observer {
    public:
    void Update(Object* sender) {
        /* Do something with the sender */
    }
};

Practical considerations

  • Practically the observer pattern has some implementation dependent flaws (e.g. a framework or a custom implementation)
  • Main problem: How to cleanly detach an event listener?
  • In managed languages memory leaks can occur
  • Here weak references provide a way out of this mess
  • In native languages segmentation faults are possible
  • Hence: Always think about how (and when) to remove the observer

A stock ticker

public struct Stock
{
    public double Price { get; set; }
    public string Code { get; set; }
}
public interface IStockObserverBase
{
    void Notify(Stock stock);
}
public class SpecificStockObserver : IStockObserverBase
{
    public SpecificStockObserver(string name)
    {
        Name = name;
    }

    public string Name
    {
        get;
        private set;
    }

    public void Notify(Stock stock)
    {
        if(stock.Code == Name)
            Console.WriteLine("{0} changed to {1:C}", stock.Code, stock.Price);
    }
}
public abstract class StockTickerBase
{
    readonly protected List<IStockObserverBase> _observers = new List<IStockObserverBase>();

    public void Register(IStockObserverBase observer)
    {
        if(!_observers.Contains(observer))
        {
            _observers.Add(observer);
        }
    }

    public void Unregister(IStockObserverBase observer)
    {
        if (_observers.Contains(observer))
        {
            _observers.Remove(observer);
        }
    }
}
public class StockTicker : StockTickerBase
{
    private List<Stock> stocks = new List<Stock>();

    public void Change(string code, double price)
    {
        Stock stock = new Stock
        {
            Code = code,
            Price = price
        };

        if (stocks.Contains(stock))
            stocks.Remove(stock);

        stocks.Add(stock);
        Notify(stock);
    }

    void Notify(Stock stock)
    {
        foreach (var observer in _observers)
        {
            observer.Notify(stock);
        }
    }
}

The command pattern

  • Commands are important and exist in various forms
  • They could be command line arguments or input, events, ...
  • Today they are mostly present by clicking on buttons in GUI
  • The command pattern defines four terms:
    • command (the central object)
    • receiver (contains specific method(s))
    • invoker (uses the command for invocation)
    • client (knows when to pass the command to the invoker)

An illustrative example

  • As an example let's take a simple application with a GUI
  • The client would be a Button
  • There are multiple invokers like a Click
  • Each invoker has a number of commands defined
  • A command connects the defined invoker of an existing Button with our own implementation (receiver)
  • The command is therefore something like an abstraction of the underlying implementation

Command diagram

skinparam classAttributeIconSize 0
interface Command {
  {abstract} +execute()
}
class ConcreteCommand {
  +execute()
}
class Receiver {
  +someAction()
}
class Invoker {
  +commands
  +invoke()
}
ConcreteCommand -.-|> Command
ConcreteCommand -> Receiver
Invoker o-> Command
Client -> Receiver
Client -.-> ConcreteCommand

Remarks

  • The command pattern is quite similar to the factory method, but instead of creation it is about execution
  • The terminology is not consistent and often confusing
  • Implementations might consider having do and undo instead of execute
  • Also a Boolean indicator if the command can be executed might make sense (usually this is a get method)
  • Commands are a great extension to the observer pattern

Light commands

public interface ICommand
{
    void Execute();
}
public class Switch
{
    private List<ICommand> _commands = new List<ICommand>();

    public void StoreAndExecute(ICommand command)
    {
        _commands.Add(command);
        command.Execute();
    }
}
public class Light
{
    public void TurnOn()
    {
        Console.WriteLine("The light is on");
    }

    public void TurnOff()
    {
        Console.WriteLine("The light is off");
    }
}
public class FlipUpCommand : ICommand
{
    private Light _light;

    public FlipUpCommand(Light light)
    {
        _light = light;
    }

    public void Execute()
    {
        _light.TurnOn();
    }
}
public class FlipDownCommand : ICommand
{
    private Light _light;

    public FlipDownCommand(Light light)
    {
        _light = light;
    }

    public void Execute()
    {
        _light.TurnOff();
    }
}
public interface Command {
    void execute();
}
public class Switch {
    private List<Command> history = new ArrayList<Command>();

    public Switch() {
    }
 
    public void storeAndExecute(Command command) {
        this.history.add(command);
        command.execute();          
    }
}
public class Light {
    public Light() {
    }
 
    public void turnOn() {
        System.out.println("The light is on");
    }
 
    public void turnOff() {
        System.out.println("The light is off");
    }
}
public class FlipUpCommand implements Command {
    private Light theLight;
 
    public FlipUpCommand(Light light) {
        this.theLight = light;
    }
 
    public void execute(){
        theLight.turnOn();
    }
}
public class FlipDownCommand implements Command {
    private Light theLight;
 
    public FlipDownCommand(Light light) {
        this.theLight = light;
    }
 
    public void execute() {
        theLight.turnOff();
    }
}
class Command {
    public:
    virtual void Execute() = 0;
};
class Switch {
    private:
    list<Command*>* history;

    public:
    Switch() {
        history = new list<Command*>();
    }
 
    public void StoreAndExecute(Command* command) {
        history->push_back(command);
        command->execute();          
    }
};
class Light {
    public:
    Light() {
    }
 
    void TurnOn() {
        cout << "The light is on" << endl;
    }
 
    void TurnOff() {
        cout << "The light is off" << endl;
    }
};
class FlipUpCommand : public Command {
    private:
    Light* theLight;
 
    public:
    FlipUpCommand(Light* light) {
        theLight = light;
    }
 
    void Execute() {
        theLight->TurnOn();
    }
};
class FlipDownCommand : public Command {
    private:
    Light* theLight;
 
    public:
    FlipDownCommand(Light* light) {
        theLight = light;
    }
 
    void Execute() {
        theLight->TurnOff();
    }
};

Practical considerations

  • There are some points when the command pattern is really useful:
    • A history of requests is needed
    • Callback functionality is required
    • Requests are handled at variant times / orders
    • Invoker and client should be decoupled
  • Commands are useful for wizards, progress bars, GUI buttons, menu actions, and other transactional behavior

Controlling a robot

public abstract class RobotCommandBase
{
    protected Robot _robot;

    public RobotCommandBase(Robot robot)
    {
        _robot = robot;
    }

    public abstract void Execute();

    public abstract void Undo();
}
public class MoveCommand:RobotCommandBase
{
    public int ForwardDistance { get; set; }

    public MoveCommand(Robot robot) : base(robot) { }

    public override void Execute()
    {
        _robot.Move(ForwardDistance);
    }

    public override void Undo()
    {
        _robot.Move(-ForwardDistance);
    }
}
public class RotateLeftCommand : RobotCommandBase
{
    public double LeftRotationAngle { get; set; }

    public RotateLeftCommand(Robot robot) : base(robot) { }

    public override void Execute()
    {
        _robot.RotateLeft(LeftRotationAngle);
    }

    public override void Undo()
    {
        _robot.RotateRight(LeftRotationAngle);
    }
}
public class RotateRightCommand : RobotCommandBase
{
    public double RightRotationAngle { get; set; }

    public RotateRightCommand(Robot robot) : base(robot) { }

    public override void Execute()
    {
        _robot.RotateRight(RightRotationAngle);
    }
 
    public override void Undo()
    {
        _robot.RotateLeft(RightRotationAngle);
    }
}
public class TakeSampleCommand : RobotCommandBase
{
    public bool TakeSample { get; set; }

    public TakeSampleCommand(Robot robot) : base(robot) { }

    public override void Execute()
    {
        _robot.TakeSample(true);
    }

    public override void Undo()
    {
        _robot.TakeSample(false);
    }
}
public class RobotController
{
    private Queue<RobotCommandBase> commands;
    private Stack<RobotCommandBase> undoStack;

    public RobotController()
    {
        commands = new Queue<RobotCommandBase>();
        undoStack = new Stack<RobotCommandBase>();
    }

    public void EnqueueCommand(RobotCommandBase command)
    {
        commands.Enqueue(command);
    }

    public void ClearCommands()
    {
        commands.Clear();
    }

    public void ExecuteAllCommands()
    {
        Console.WriteLine("EXECUTING COMMANDS.");

        while (commands.Count > 0)
        {
            RobotCommandBase command = commands.Dequeue();
            command.Execute();
            undoStack.Push(command);
        }
    }

    public void UndoCommands(int numUndos)
    {
        Console.WriteLine("REVERSING {0} COMMAND(S).", numUndos);

        while (numUndos > 0 && undoStack.Count > 0)
        {
            RobotCommandBase command = undoStack.Pop();
            command.Undo();
            numUndos--;
        }
    }
}
public class Robot
{
    public void Move(int distance)
    {
        if (distance > 0)
            Console.WriteLine("Robot moved forwards {0}mm.", distance);
        else
            Console.WriteLine("Robot moved backwards {0}mm.", -distance);
    }

    public void RotateLeft(double angle)
    {
        if (angle > 0)
            Console.WriteLine("Robot rotated left {0} degrees.", angle);
        else
            Console.WriteLine("Robot rotated right {0} degrees.", -angle);
    }

    public void RotateRight(double angle)
    {
        if (angle > 0)
            Console.WriteLine("Robot rotated right {0} degrees.", angle);
        else
            Console.WriteLine("Robot rotated left {0} degrees.", -angle);
    }

    public void TakeSample(bool take)
    {
        if(take)
            Console.WriteLine("Robot took sample");
        else
            Console.WriteLine("Robot released sample");
    }
}

The chain-of-responsibility pattern

  • Quite often we want to execute various commands in a certain way
  • Creating a chain of responsibility ensures a loosely coupled system
  • The chain uses a source of (atomic) commands a series of processing objects, which contain the logic
  • Classically the pattern defines a linked list of handlers
  • The direction is not fixed, it could also be a tree of responsibility
  • In general this is a perfect extension to the command pattern

Chain-of-responsibility diagram

skinparam classAttributeIconSize 0
class Handler {
  +successor: Handler
  +handle()
}
Client -> Handler
ConcreteHandlerA --|> Handler
ConcreteHandlerB --|> Handler
Handler o-> "0..1" Handler

Remarks

  • The command is passed to the first processing object which can handle this command or send to its successor
  • A single handler only needs to know its successor, if any
  • This is a big plus, but might lead to a circle (and infinite loop)
  • Also the chain is only as good as its weakest member
  • This means that if the last handler is not responsible for the request, it will not execute the build chain of commands

A simple sample

public abstract class HandlerBase
{
    public HandlerBase Successor
    {
        get;
        set;
    }

    public abstract void HandleRequest(int request);
}
public class ConcreteHandlerA : HandlerBase
{
    public override void HandleRequest(int request)
    {
        if (request == 1)
            Console.WriteLine("Handled by ConcreteHandlerA");
        else if (Successor != null)
            Successor.HandleRequest(request);
    }
}
public class ConcreteHandlerB : HandlerBase
{
    public override void HandleRequest(int request)
    {
        if (request > 10)
            Console.WriteLine("Handled by ConcreteHandlerB");
        else if (Successor != null)
            Successor.HandleRequest(request);
    }
}
public abstract class HandlerBase {
    private Handlerbase successor;

    public HandlerBase getSuccessor() {
        return successor;
    }

    public void setSuccessor(HandlerBase value) {
        successor = value;
    }

    public abstract void handleRequest(int request);
}
public class ConcreteHandlerA extends HandlerBase {
    @Override
    public void handleRequest(int request) {
        if (request == 1)
            out.println("Handled by ConcreteHandlerA");
        else if (getSuccessor() != null)
            getSuccessor().handleRequest(request);
    }
}
public class ConcreteHandlerB extends HandlerBase {
    @Override
    public void handleRequest(int request) {
        if (request > 10)
            out.println("Handled by ConcreteHandlerB");
        else if (getSuccessor() != null)
            getSuccessor().handleRequest(request);
    }
}
class HandlerBase {
    private:
    Handlerbase* successor;

    public:
    HandlerBase* GetSuccessor() {
        return successor;
    }

    void SetSuccessor(HandlerBase* value) {
        successor = value;
    }

    virtual void HandleRequest(int request) = 0;
};
class ConcreteHandlerA : public HandlerBase {
    public:
    void HandleRequest(int request) {
        if (request == 1)
            cout << "Handled by ConcreteHandlerA" << endl;
        else if (GetSuccessor() != NULL)
            getSuccessor()->HandleRequest(request);
    }
};
class ConcreteHandlerB : public HandlerBase {
    public:
    void HandleRequest(int request) {
        if (request > 10)
            cout << "Handled by ConcreteHandlerB" << endl;
        else if (GetSuccessor() != NULL)
            getSuccessor()->HandleRequest(request);
    }
};

Practical considerations

  • Use this pattern if more than one handler for a request is available
  • Otherwise if one handler might require another handler
  • Or if the set of handlers varies dynamically
  • Chain-of-responsibility patterns are great for filtering
  • The biggest advantage is the extensibility
  • Also the specific handler does not have to be known (information hiding)

Chain-of-responsibility Vs Command

Chain-of-responsibilityCommand
Client creates Handlers Commands
Variations of Handlers Commands, receivers
Clients can use Multiple handlers Different receivers
Client calls Handlers Receivers
Work done in Handler Receiver
Decisions based on Limits in handlers Routing in commands
Unknown requests? Received, not handled Executes nothing

A coin machine

public class Coin
{
    public float Weight { get; set; }
    public float Diameter { get; set; }
}
public enum CoinEvaluationResult
{
    Accepted,
    Rejected
}
public abstract class CoinHandlerBase
{
    protected CoinHandlerBase _successor;

    public void SetSuccessor(CoinHandlerBase successor)
    {
        _successor = successor;
    }

    public abstract CoinEvaluationResult EvaluateCoin(Coin coin);
}
class FiftyPenceHandler : CoinHandlerBase
{
    public override CoinEvaluationResult EvaluateCoin(Coin coin)
    {
        if (Math.Abs(coin.Weight - 8) < 0.02 && Math.Abs(coin.Diameter - 27.3) < 0.15)
        {
            Console.WriteLine("Captured 50p");
            return CoinEvaluationResult.Accepted;
        }
        if (_successor != null)
        {
            return _successor.EvaluateCoin(coin);
        }
        return CoinEvaluationResult.Rejected;
    }
}
class FivePenceHandler : CoinHandlerBase
{

    public override CoinEvaluationResult EvaluateCoin(Coin coin)
    {
        if (Math.Abs(coin.Weight - 3.25) < 0.02 && Math.Abs(coin.Diameter - 18) < 0.1)
        {
            Console.WriteLine("Captured 5p");
            return CoinEvaluationResult.Accepted;
        }
        if (_successor != null)
        {
            return _successor.EvaluateCoin(coin);
        }
        return CoinEvaluationResult.Rejected;
    }
}
class OnePoundHandler : CoinHandlerBase
{
    public override CoinEvaluationResult EvaluateCoin(Coin coin)
    {
        if (Math.Abs(coin.Weight - 9.5) < 0.02 && Math.Abs(coin.Diameter - 22.5) < 0.13)
        {
            Console.WriteLine("Captured £1");
            return CoinEvaluationResult.Accepted;
        }
        if (_successor != null)
        {
            return _successor.EvaluateCoin(coin);
        }
        return CoinEvaluationResult.Rejected;
    }
}
class TenPenceHandler : CoinHandlerBase
{
    public override CoinEvaluationResult EvaluateCoin(Coin coin)
    {
        if (Math.Abs(coin.Weight - 6.5) < 0.03 && Math.Abs(coin.Diameter - 24.5) < 0.15)
        {
            Console.WriteLine("Captured 10p");
            return CoinEvaluationResult.Accepted;
        }
        if (_successor != null)
        {
            return _successor.EvaluateCoin(coin);
        }
        return CoinEvaluationResult.Rejected;
    }
}
class TwentyPenceHandler : CoinHandlerBase
{
    public override CoinEvaluationResult EvaluateCoin(Coin coin)
    {
        if (Math.Abs(coin.Weight - 5) < 0.01 && Math.Abs(coin.Diameter - 21.4) < 0.1)
        {
            Console.WriteLine("Captured 20p");
            return CoinEvaluationResult.Accepted;
        }
        if (_successor != null)
        {
            return _successor.EvaluateCoin(coin);
        }
        return CoinEvaluationResult.Rejected;
    }
}

The iterator pattern

  • Sometimes we want to iterate over a bunch of objects
  • This is actually like handling a linked list, e.g. the previous pattern
  • Traversing lists, trees, and other structures is important
  • Key: Iterating without knowing the explicit structure
  • Goal: Provide a simple interface for traversing a collection of items
  • Languages like C# or Java have the iterator pattern build in

Iterator diagram

skinparam classAttributeIconSize 0
abstract class Enumerable {
  {abstract} +getEnumerator(): Iterator
}
class ConcreteEnumerable {
  +getEnumerator(): Iterator
}
abstract class Iterator {
  {abstract} +current(): object
  {abstract} +next(): bool
}
class ConcreteIterator {
  +current(): object
  +next(): bool
}
ConcreteEnumerable -|> Enumerable
Client -> Enumerable
Client --> Iterator
ConcreteIterator -|> Iterator
ConcreteEnumerable -.-> ConcreteIterator

Remarks

  • In C++ the pattern is usually implemented with pointer operators
  • Java and C# already provide an interface to implement
  • Usually there is a generic interface, which should be used
  • The iterator pattern enables technologies such as LINQ
  • Passing iterators as arguments can be very efficiently
  • Reason: The iterator object maintains the state of the iteration

Trivial iterator

interface IEnumerator 
{
    object Current { get; }
    bool MoveNext();
    void Reset();
}

interface IEnumerable
{
    IEnumerator GetEnumerator();
}

class MyClass : IEnumerable
{
    object[] array;

    /* ... */

    public IEnumerator GetEnumerator()
    {
        return new ArrayIterator(array);
    }
}

class ArrayIterator : IEnumerator
{
    int index;
    object[] array;

    public ArrayIterator (object[] array)
    {
		this.array = array;
		Reset();
    }

    public object Current
    {
        get { return index >= 0 && index < array.Length ? array[index] : null; }
    }

    public bool MoveNext()
    {
        index++;
        return Current != null;
    }

    public void Reset()
    {
        index = -1;
    }
}
interface Enumerator {
    object getCurrent();
    bool moveNext();
    void reset();
}

interface Enumerable {
    Enumerator getEnumerator();
}

class MyClass implements Enumerable {
    object[] array;

    /* ... */

    public Enumerator getEnumerator() {
        return new ArrayIterator(array);
    }
}

class ArrayIterator implements Enumerator {
    int index;
    object[] array;

    public ArrayIterator (object[] array) {
        this.array = array;
        Reset();
    }

    public object getCurrent() {
        return index >= 0 && index < array.Length ? array[index] : null;
    }

    public bool moveNext() {
        index++;
        return Current != null;
    }

    public void reset() {
        index = -1;
    }
}

Practical considerations

  • C# and Java have a loop construct based on the iterator pattern
  • Here GetEnumerator() is called implicitely
  • Then MoveNext() is used until false is returned
  • This makes foreach() and for(:) more expensive than the classic for loop, which does not require any function calls
  • Advantage: We can iterate over arbitrary objects
  • In C++ we can always use pointer overloads to achieve this

A simple stack iterator

class Stack {
    int items[10];
    int sp;

    public:
    friend class StackIter;

    Stack() {
        sp = -1;
    }

    void Push(int in) {
        items[++sp] = in;
    }

    int Pop() {
        return items[sp--];
    }

    bool IsEmpty() {
        return (sp == - 1);
    }

    StackIterator* CreateIterator() const {
        return new StackIterator(this);
    }
};


class StackIterator {
    const Stack *stk;
    int index;

    public:
    StackIter(const Stack *s) {
        stk = s;
    }

    void Reset() {
        index = -1;
    }

    bool MoveNext() {
        return ++index == stk->sp + 1;
    }

    int GetCurrentItem() {
        return stk->items[index];
    }
};

Visitor pattern

  • Separates a set of structured data from the functionality
  • Promotes loose coupling
  • Enables additional operations
  • However, the data model (structure) is therefore really limited
  • Basically it is just a container, which requires somebody to visit and evaluate its data

Visitor diagram

skinparam classAttributeIconSize 0
interface Visitor {
  +visit(Object)
}

class ConcreteVisitor {
  +visit(Object)
}

Client --> Visitor
Client --> Element
ConcreteVisitor .-|> Visitor

abstract class Element {
  +{abstract} accept(Object)
}

class ConcreteElement extends Element {
  +accept(Object)
}

Remarks

  • New operations are added by creating new visitors
  • Similar operations are managed in one visitor and separated from others
  • A visitor can work over multiple class hierarchies
  • However, the nice extensibility with visitors comes with greater constraints for the specific elements
  • Compilers usually traverse the object tree via the visitor pattern
  • In general the visitor pattern helps to apply operations on structures without information on the structure

Visitor sample

abstract class Visitor
{
    public abstract void VisitConcreteElementA(ConcreteElementA concreteElementA);

    public abstract void VisitConcreteElementB(ConcreteElementB concreteElementB);
}

class ConcreteVisitor : Visitor
{
    public override void VisitConcreteElementA(ConcreteElementA concreteElementA)
    {
        /* ... */
    }

    public override void VisitConcreteElementB(ConcreteElementB concreteElementB)
    {
        /* ... */
    }
}

abstract class Element
{
    public abstract void Accept(Visitor visitor);
}

class ConcreteElementA : Element
{
    public override void Accept(Visitor visitor)
    {
        visitor.VisitConcreteElementA(this);
    }
}

class ConcreteElementB : Element
{
    public override void Accept(Visitor visitor)
    {
        visitor.VisitConcreteElementB(this);
    }
}

class ObjectStructure
{
    private List<Element> _elements;

    public ObjectStructure()
    {
        _elements = new List<Element>();
    }

    public void Attach(Element element)
    {
        _elements.Add(element);
    }

    public void Detach(Element element)
    {
        _elements.Remove(element);
    }

    public void Accept(Visitor visitor)
    {
        foreach (Element element in _elements)
        {
            element.Accept(visitor);
        }
    }
}
abstract class Visitor {
    public abstract void visitConcreteElementA(ConcreteElementA concreteElementA);

    public abstract void visitConcreteElementB(ConcreteElementB concreteElementB);
}

class ConcreteVisitor extends Visitor {
    @Override
    public void visitConcreteElementA(ConcreteElementA concreteElementA) {
        /* ... */
    }

    @Override
    public void visitConcreteElementB(ConcreteElementB concreteElementB) {
        /* ... */
    }
}

abstract class Element {
    public abstract void accept(Visitor visitor);
}

class ConcreteElementA extends Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visitConcreteElementA(this);
    }
}

class ConcreteElementB extends Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visitConcreteElementB(this);
    }
}

class ObjectStructure {
    private ArrayList<Element> _elements;

    public ObjectStructure() {
        _elements = new ArrayList<Element>();
    }

    public void attach(Element element) {
        _elements.add(element);
    }

    public void detach(Element element) {
        _elements.remove(element);
    }

    public void accept(Visitor visitor) {
        for (Element element : _elements) {
            element.accept(visitor);
        }
    }
}
class Visitor {
    public:
    virtual void VisitConcreteElementA(ConcreteElementA* concreteElementA) = 0;

    virtual void VisitConcreteElementB(ConcreteElementB* concreteElementB) = 0;
};

class ConcreteVisitor : public Visitor {
    public:
    void VisitConcreteElementA(ConcreteElementA* concreteElementA) {
        /* ... */
    }

    void VisitConcreteElementA(ConcreteElementB* concreteElementA) {
        /* ... */
    }
};

class Element {
    public:
    virtual void Accept(Visitor* visitor) = 0;
};

class ConcreteElementA : public Element {
    public:
    void Accept(Visitor* visitor) {
        visitor->VisitConcreteElementA(this);
    }
};

class ConcreteElementB : public Element {
    public:
    void Accept(Visitor* visitor) {
        visitor->VisitConcreteElementB(this);
    }
};

class ObjectStructure {
    private:
    list<Element*>* _elements;

    public:
    ObjectStructure() {
        _elements = new list<Element*>();
    }

    void Attach(Element* element) {
        _elements->push_back(element);
    }

    void Detach(Element* element) {
        _elements->remove(element);
    }

    void Accept(Visitor* visitor) {
        for (list<Element*>::iterator it = _elements->begin(); it != _elements->end(); ++it) {
            (*it)->Accept(visitor);
        }
    }
};

Practical considerations

  • The iterator pattern and visitor pattern has the same benefit, they are used to traverse object structures
  • The visitor pattern can be used on complex structure such as hierarchical structures or composite structures
  • Drawback: If a new visitable object is added to the framework structure all the implemented visitors need to be modified
  • Part of the dependency problems can be solved by using reflection with a performance cost

Operations on employees

interface IVisitor
{
    void Visit(Element element);
}

class IncomeVisitor : IVisitor
{
    public void Visit(Element element)
    {
        Employee e = element as Employee;

        if (e != null)
        {
            e.Income *= 1.10;
            Console.WriteLine("{0} {1}'s new income: {2:C}", e.GetType().Name, e.Name, e.Income);
        }
    }
}

class VacationVisitor : IVisitor
{
    public void Visit(Element element)
    {
        Employee e = element as Employee;

        if (e != null)
        {
            Console.WriteLine("{0} {1}'s new vacation days: {2}", e.GetType().Name, e.Name, e.VacationDays);
        }
    }
}

abstract class Element
{
    public abstract void Accept(IVisitor visitor);
}

class Employee : Element
{
    private string _name;
    private double _income;
    private int _vacationDays;
  
    public Employee(string name, double income, int vacationDays)
    {
        this._name = name;
        this._income = income;
        this._vacationDays = vacationDays;
    }
  
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
  
    public double Income
    {
        get { return _income; }
        set { _income = value; }
    }
  
    public int VacationDays
    {
        get { return _vacationDays; }
        set { _vacationDays = value; }
    }
  
    public override void Accept(IVisitor visitor)
    {
        visitor.Visit(this);
    }
}

class Employees
{
    private List<Employee> _employees;

    public Employees()
    {
        _employees = new List<Employee>();
    }

    public void Attach(Employee employee)
    {
        _employees.Add(employee);
    }

    public void Detach(Employee employee)
    {
        _employees.Remove(employee);
    }

    public void Accept(IVisitor visitor)
    {
        foreach (Employee e in _employees)
        {
            e.Accept(visitor);
        }
    }
}

class Clerk : Employee
{
    public Clerk() : base("Hank", 25000.0, 14)
    {
    }
}

class Director : Employee
{
    public Director() : base("Elly", 35000.0, 16)
    {
    }
}

class President : Employee
{
    public President() : base("Dick", 45000.0, 21)
    {
    }
}

State pattern

  • Classes have responsibilities and contain logic, but sometimes the logic can be messy
  • A special case is a state-machine, where most logic is dependent on the current state
  • The state pattern tries to simplify such a logic
  • Idea: Encapsulate the state-dependent logic units as classes
  • This allows to change behavior at run-time without changing the interface used to access the object

State diagram

skinparam classAttributeIconSize 0
class Context {
  -state
  +request()
}
abstract class State {
  {abstract} +handle(Context)
}
class ConcreteStateA extends State {
  +handle(Context)
}
class ConcreteStateB extends State {
  +handle(Context)
}
Context o-> "1" State

Remarks

  • The context holds the concrete state object that provides the behavior according to its current state
  • This pattern closely resembles the strategy pattern (upcoming)
  • A default behavior has to be defined (i.e. which state to use initially)
  • Sometimes one state wants to change itself - this is not possible directly
  • Here we could either make the state variable in the Context internal or public (or friend in C++) or return an element of type State

State sample

public class Context
{
    private StateBase _state;

    public Context(StateBase state)
    {
        State = state;
    }

    public void Request()
    {
        _state.Handle(this);
    }

    public StateBase State
    {
        get { return _state; }
        set { _state = value; }
    }
}

public abstract class StateBase
{
    public abstract void Handle(Context context);
}

class ConcreteStateA : StateBase
{
    public override void Handle(Context context)
    {
        context.State = new ConcreteStateB();
    }
}

class ConcreteStateB : StateBase
{
    public override void Handle(Context context)
    {
        context.State = new ConcreteStateA();
    }
}
public class Context {
    private StateBase _state;

    public Context(StateBase state) {
        setState(state);
    }

    public void request() {
        _state.handle(this);
    }

    public StateBase getState() {
        return _state;
    }

    public void setState(StateBase state) {
        _state = state;
    }
}

public abstract class StateBase {
    public abstract void handle(Context context);
}

class ConcreteStateA extends StateBase {
    @Override
    public void handle(Context context) {
        context.setState(new ConcreteStateB());
    }
}

class ConcreteStateB extends StateBase {
    @Override
    public void handle(Context context) {
        context.setState(new ConcreteStateA());
    }
}
class Context {
    private:
    StateBase* _state;

    public:
    Context(StateBase* state) {
        SetState(state);
    }

    void Request() {
        _state->Handle(this);
    }

    StateBase* GetState() {
        return _state;
    }

    void SetState(StateBase* state) {
        _state = state;
    }
};

class StateBase {
    public:
    virtual void Handle(Context* context) = 0;
};

class ConcreteStateA : public StateBase {
    public:
    void Handle(Context* context) {
        context->SetState(new ConcreteStateB());
    }
};

class ConcreteStateB : public StateBase {
    public:
    void Handle(Context* context) {
        context->SetState(new ConcreteStateA());
    }
};

Practical considerations

  • Instantiating states can be unnecessary
  • Hence buffering state instances might be interesting
  • In such cases the factory pattern might be helpful
  • States also provide a simple mechanism for extending existing behavior
  • We will see that similar patterns, like the template pattern or the strategy patterns exist

A DVD player

public abstract class DVDPlayerState
{
    public abstract DVDPlayerState PlayButtonPressed(DVDPlayer player);

    public abstract DVDPlayerState MenuButtonPressed(DVDPlayer player);
}

public class DVDPlayer
{
    private DVDPlayerState _state;

    public DVDPlayer() : this(new StandbyState())
    {
    }

    public DVDPlayer(DVDPlayerState state)
    {
        _state = state;
    }

    public void PressPlayButton()
    {
        _state = _state.PlayButtonPressed(this);
    }

    public void PressMenuButton()
    {
        _state = _state.MenuButtonPressed(this);
    }
}

class MenuState : DVDPlayerState
{
    public MenuState()
    {
        Console.WriteLine("MENU");
    }

    public override void PlayButtonPressed(DVDPlayer player)
    {
        Console.WriteLine("  Next Menu Item Selected");
        return this;
    }

    public override void MenuButtonPressed(DVDPlayer player)
    {
        return new StandbyState();
    }
}

class MoviePausedState : DVDPlayerState
{
    public MoviePausedState()
    {
        Console.WriteLine("MOVIE PAUSED");
    }

    public override DVDPlayerState PlayButtonPressed(DVDPlayer player)
    {
        return new MoviePlayingState();
    }

    public override DVDPlayerState MenuButtonPressed(DVDPlayer player)
    {
        return new MenuState();
    }
}

class MoviePlayingState : DVDPlayerState
{
    public MoviePlayingState()
    {
        Console.WriteLine("MOVIE PLAYING");
    }
 
    public override DVDPlayerState PlayButtonPressed(DVDPlayer player)
    {
        return new MoviePausedState();
    }

    public override DVDPlayerState MenuButtonPressed(DVDPlayer player)
    {
        return new MenuState();
    }
}

class StandbyState : DVDPlayerState
{
    public StandbyState()
    {
        Console.WriteLine("STANDBY");
    }

    public override DVDPlayerState PlayButtonPressed(DVDPlayer player)
    {
        return new MoviePlayingState();
    }

    public override DVDPlayerState MenuButtonPressed(DVDPlayer player)
    {
        return new MenuState();
    }
}

... and the strategy pattern

skinparam classAttributeIconSize 0
class Context {
}
interface Strategy {
  +execute()
}
class ConcreteStrategyA implements Strategy {
  +execute()
}
class ConcreteStrategyB implements Strategy {
  +execute()
}
Context *-> Strategy

Comparison

  • The state pattern is usually quite tightly coupled to a context
  • The strategy pattern is completely independent of a context and provides one solution to a problem
  • Strategy lets the algorithm vary independently from clients that use it
  • A common problem: Choosing one (of many) hashing algorithms
  • Solution: An interface that defines the method and various implementations (e.g. HAVAL, MD5, SHA1, SHA2, ...)

... and the template pattern

  • Quite similar to the state / strategy pattern is the template pattern
  • This pattern is based on an abstract class, which defines a set of methods that need to be implemented
  • Those methods are then called in a fixed sequence by some client
  • While the Strategy design pattern overrides the entire algorithm, this pattern allows us to change only some parts of the behavior algorithm using abstract operations

Example template sequence

participant "Core" as A
participant "Routing" as B
participant "Controller" as C
[-> A: Request
activate A
A -> B: GetRoute
activate B
create C
B -> C: Create
B --> A: Controller
deactivate B
A -> C: Initialize
activate C
deactivate C
A -> C: BeginExecute
activate C
deactivate C
A -> C: Action
activate C
C --> A: ActionResult
deactivate C
A -> C: EndExecute
activate C
deactivate C
A -> C: Finalize
activate C
deactivate C
destroy C

Memento pattern

  • Saving or restoring states is quite important (e.g. undo, redo)
  • The memento pattern defines a memento, a caretaker and an originator
  • The originator wants to save or restore his state
  • The whole state information is stored in an instance of a class, called memento
  • The class responsible for managing the various states is the caretaker

Memento diagram

skinparam classAttributeIconSize 0
class Originator {
  -state: Memento
  +store(): Memento
  +restore(Memento)
}

class Memento {
  -state1
  -state2
  -stateN
  +getState1()
  +getState2()
  +getStateN()
  +Memento(state1, state2, stateN)
}

Originator .> Memento
Memento <-o Caretaker

Remarks

  • The memento is opaque to the caretaker, and the caretaker must not operate on it
  • The memento must not allow any operation or access to the internal state
  • However, the originator has to access the internal information for restoration
  • The memento contains everything that is required for restoring a state

Memento sample

class Originator
{
    private String state;
 
    public void Set(String state)
    {
        this.state = state;
    }
 
    public Memento SaveToMemento()
    {
        return new Memento(state);
    }
 
    public void RestoreFromMemento(Memento memento)
    {
        state = memento.GetSavedState();
    }
}
 
public class Memento
{
    private readonly String state;

    public Memento(String stateToSave)
    {
        state = stateToSave;
    }

    public String GetSavedState()
    {
        return state;
    }
}
 
class Caretaker 
{
    List<Memento> savedStates;
    Originator originator;

    public Caretaker()
    {
        savedStates = new List<Memento>();
        originator = new Originator();
    }

    public void ExampleAction()
    {
        originator.Set("State1");
        originator.Set("State2");
        savedStates.Add(originator.SaveToMemento());
        originator.Set("State3");
        savedStates.Add(originator.SaveToMemento());
        originator.Set("State4");
        originator.RestoreFromMemento(savedStates[1]);   
    }
}
class Originator {
    private String state;
 
    public void set(String state) {
        this.state = state;
    }
 
    public Memento saveToMemento() {
        return new Memento(state);
    }
 
    public void restoreFromMemento(Memento memento) {
        state = memento.getSavedState();
    }
}

public class Memento {
    private final String state;

    public Memento(String stateToSave) {
        state = stateToSave;
    }

    public String getSavedState() {
        return state;
    }
}
 
class Caretaker {
    List<Memento> savedStates;
    Originator originator;

    public Caretaker() {
        savedStates = new ArrayList<Memento>();
        originator = new Originator();
    }

    public void exampleAction() {
        originator.set("State1");
        originator.set("State2");
        savedStates.add(originator.saveToMemento());
        originator.set("State3");
        savedStates.add(originator.saveToMemento());
        originator.set("State4");
        originator.restoreFromMemento(savedStates.get(1));   
    }
}
class Originator {
    private:
    String state;
 
    public:
    void Set(String state) {
        this->state = state;
    }
 
    Memento* SaveToMemento() {
        return new Memento(state);
    }
 
    void RestoreFromMemento(Memento* memento) {
        state = memento->GetSavedState();
    }
};
 
class Memento {
    private:
    const String state;

    public:
    Memento(String stateToSave) : state(stateToSave) {
    }

    public String GetSavedState() {
        return state;
    }
};
 
class Caretaker {
    vector<Memento*>* savedStates;
    Originator* originator;

    public:
    Caretaker() {
        savedStates = new vector<Memento*>();
        originator = new Originator();
    }

    void ExampleAction() {
        originator->Set("State1");
        originator->Set("State2");
        savedStates->push_back(originator.SaveToMemento());
        originator->Set("State3");
        savedStates->push_back(originator.SaveToMemento());
        originator->Set("State4");
        originator->RestoreFromMemento(savedStates->at(1));   
    }
};

Specification pattern

  • Most applications will be defined by set of rules
  • Those rules are usually called business rules or business logic
  • The specification pattern allows chaining or recombining objects, which are included in our business logic
  • Usually this is build upon three logical operations (and, or, not)
  • The classes itself (logic units) will decide if they are applicable
  • Chaining is very important for this pattern

Specification diagram

skinparam classAttributeIconSize 0
interface Specification {
  +isSatisfiedBy(candidate): Boolean 
  +and(): Specification
  +not(): Specification
  +or(): Specification
}
abstract class CompositeSpecification implements Specification {
  +{abstract} isSatisfiedBy(candidate): Boolean 
  +and(): Specification
  +not(): Specification
  +or(): Specification
}
class AndSpecification extends CompositeSpecification {
  +isSatisfiedBy(candidate): Boolean 
  +one: Specification
  +other: Specification
}
class OrSpecification extends CompositeSpecification {
  +isSatisfiedBy(candidate): Boolean 
  +one: Specification
  +other: Specification
}
class NotSpecification extends CompositeSpecification {
  +isSatisfiedBy(candidate): Boolean
  +wrapped: Specification 
}

Remarks

  • The pattern can be used to remove a lot of cruft from a class's interface while decreasing coupling and increasing extensibility
  • The primary use is to select a subset of objects based on some criteria
  • Languages that allow delegates / function pointers in combination with generics may be already one step ahead
  • The pattern's root is in Domain Driven Design for criteria tests
  • Sometimes data structures are more complex and the visitor pattern might be a useful addition

Implementation in C#

public interface ISpecification<TEntity>
{
    bool IsSatisfiedBy(TEntity entity);
}

internal class AndSpecification<TEntity> : ISpecification<TEntity>
{
    private readonly ISpecification<TEntity> _spec1;
    private readonly ISpecification<TEntity> _spec2;

    protected ISpecification<TEntity> Spec1
    {
        get { return _spec1; }
    }

    protected ISpecification<TEntity> Spec2
    {
        get { return _spec2; }
    }

    internal AndSpecification(ISpecification<TEntity> spec1, ISpecification<TEntity> spec2)
    {
        if (spec1 == null)
            throw new ArgumentNullException("spec1");
        else if (spec2 == null)
            throw new ArgumentNullException("spec2");

        _spec1 = spec1;
        _spec2 = spec2;
    }

    public bool IsSatisfiedBy(TEntity candidate)
    {
        return Spec1.IsSatisfiedBy(candidate) && Spec2.IsSatisfiedBy(candidate);
    }
}

internal class OrSpecification<TEntity> : ISpecification<TEntity>
{
    private readonly ISpecification<TEntity> _spec1;
    private readonly ISpecification<TEntity> _spec2;

    protected ISpecification<TEntity> Spec1
    {
        get { return _spec1; }
    }

    protected ISpecification<TEntity> Spec2
    {
        get { return _spec2; }
    }

    internal OrSpecification(ISpecification<TEntity> spec1, ISpecification<TEntity> spec2)
    {
        if (spec1 == null)
            throw new ArgumentNullException("spec1");
        else if (spec2 == null)
            throw new ArgumentNullException("spec2");

        _spec1 = spec1;
        _spec2 = spec2;
    }

    public bool IsSatisfiedBy(TEntity candidate)
    {
        return Spec1.IsSatisfiedBy(candidate) || Spec2.IsSatisfiedBy(candidate);
    }
}

internal class NotSpecification<TEntity> : ISpecification<TEntity>
{
    private readonly ISpecification<TEntity> _wrapped;

    protected ISpecification<TEntity> Wrapped
    {
        get { return _wrapped; }
    }

    internal NotSpecification(ISpecification<TEntity> spec)
    {
        if (spec == null)
            throw new ArgumentNullException("spec");

        _wrapped = spec;
    }

    public bool IsSatisfiedBy(TEntity candidate)
    {
        return !Wrapped.IsSatisfiedBy(candidate);
    }
}

public static class ExtensionMethods
{
    public static ISpecification<TEntity> And<TEntity>(this ISpecification<TEntity> spec1, ISpecification<TEntity> spec2)
    {
        return new AndSpecification<TEntity>(spec1, spec2);
    }

    public static ISpecification<TEntity> Or<TEntity>(this ISpecification<TEntity> spec1, ISpecification<TEntity> spec2)
    {
        return new OrSpecification<TEntity>(spec1, spec2);
    }

    public static ISpecification<TEntity> Not<TEntity>(this ISpecification<TEntity> spec)
    {
        return new NotSpecification<TEntity>(spec);
    }
}

Mediator pattern

  • To avoid strong coupling between two classes we can introduce a mediator class
  • The mediator knows the two classes and establishes the connection
  • Basically it is a reduction of complexity
  • The reduction makes sense if both classes are quite heavy
  • In most cases using a simple interface instead can be even better

Mediator diagram

skinparam classAttributeIconSize 0
abstract class MediatorBase {
  + {abstract} sendMessage(caller: ColleagueBase)
}
abstract class ColleagueBase {
  +primary: ColleagueA
  +secondary: ColleagueA
  +sendMessage(caller: ColleagueBase)
}
class ConcreteMediator extends MediatorBase {
  #mediator: MediatorBase
  +ColleagueBase(MediatorBase)
}
class ConcreteColleagueA extends ColleagueBase {
  +receive()
  +send()
}
class ConcreteColleagueB extends ColleagueBase {
  +receive()
  +send()
}
ColleagueBase "1" -> "1" MediatorBase
ConcreteMediator "1" --> "1" ConcreteColleagueA
ConcreteMediator "1" --> "1" ConcreteColleagueB

Remarks

  • Usually the mediator tries to be transparent
  • For testability an interface is the best solution (mocking)
  • Using a middle object decouples two classes
  • We will see that this idea is the basis for more advanced techniques
  • Basis: Reflection and compile-time decoupling by run-time wiring
  • Dependency Injection uses a class that talks to all other classes
  • The concrete classes do not have to be known

Two-way mediator sequence

participant "Colleague A" as A
participant "Mediator" as M
participant "Colleague B" as B
[-> A: SomeAction
activate A
A -> A: Send
activate A
A -> M: Redirect
activate M
M -> B: Notify
activate B
deactivate B
deactivate M
deactivate A
deactivate A
[-> B: OtherAction
activate B
B -> B: Send
activate B
B -> M: Redirect
activate M
M -> A: Notify
activate A
deactivate A
deactivate M
deactivate B
deactivate B

Interpreter pattern

  • Even simple applications might require some kind of DSL
  • A DSL (Domain-Specific Language) is a kind of language for a special kind of application
  • This is in contrast to GPL (general purpose languages)
  • There are various types of DSLs like markup, modeling or programming languages
  • Arguments of a command line might be already dynamic enough to define a DSL specification

How does the interpreter pattern help?

  • The interpreter pattern allows the grammar for such a DSL to be represented as classes
  • This (object-oriented) representation can then easily be extended
  • The whole process takes information from a Context
  • A Client creates the context and starts the interpreter by calling the interpret action
  • This action is located on an object of type Expression

Interpreter diagram

skinparam classAttributeIconSize 0
class Client {
}
class Context {
}
abstract class BaseExpression {
 +{abstract} interpret(Context)
}
class TerminalExpression extends BaseExpression {
  +interpret(Context)
}
class NonterminalExpression extends BaseExpression {
  +interpret(Context)
}
NonterminalExpression o--> BaseExpression
Client --> Context
Client -> BaseExpression

Remarks

  • The context contains information that is global to the interpreter
  • The expressions represent the units of grammar
  • The state pattern can be quite useful for parsing
  • NonterminalExpression expressions are aggregates containing one or more (further) expressions (conditionally chained)
  • The TerminalExpression implements an interpret operation associated with terminal symbols in the grammar
  • An instance is required for every terminal symbol in the given string

An interpreter for roman numbers

class Client
{
	public static int Parse(string number)
	{
		Context context = new Context(number);
		List<Expression> tree = new List<Expression>();
		tree.Add(new ThousandExpression());
		tree.Add(new HundredExpression());
		tree.Add(new TenExpression());
		tree.Add(new OneExpression());

		foreach (Expression exp in tree)
			exp.Interpret(context);

		return context.Value;
	}
}

class Context
{
	private string query;
	private int value;

	public Context(string input)
	{
		query = input;
	}

	public string Query
	{
		get { return query; }
		set { this.query = value; }
	}

	public int Value
	{
		get { return value; }
		set { this.value = value; }
	}
}

abstract class Expression
{
    public void Interpret(Context context)
    {
		if (context.Query.Length == 0)
			return;

		if (context.Query.StartsWith(Nine))
		{
			context.Value += 9 * Weight;
			context.Query = context.Query.Substring(Nine.Length);
		}
		else if (context.Query.StartsWith(Four))
		{
			context.Value += 4 * Weight;
			context.Query = context.Query.Substring(Four.Length);
		}
		else if (context.Query.StartsWith(Five))
		{
			context.Value += 5 * Weight;
			context.Query = context.Query.Substring(Five.Length);
		}

		while (context.Query.StartsWith(One))
		{
			context.Value += Weight;
			context.Query = context.Query.Substring(One.Length);
		}
    }

    public virtual string One { get { return " "; } }

    public virtual string Four { get { return " "; } }

    public virtual string Five { get { return " "; } }

    public virtual string Nine { get { return " "; } }

    public abstract int Weight { get; }
}

class ThousandExpression : Expression
{
	public override string One { get { return "M"; } }

	public override int Weight { get { return 1000; } }
}

class HundredExpression : Expression
{
	public override string One { get { return "C"; } }

	public override string Four { get { return "CD"; } }

	public override string Five { get { return "D"; } }

	public override string Nine { get { return "CM"; } }

	public override int Weight { get { return 100; } }
}

class TenExpression : Expression
{
	public override string One { get { return "X"; } }

	public override string Four { get { return "XL"; } }

	public override string Five { get { return "L"; } }

	public override string Nine { get { return "XC"; } }

	public override int Weight { get { return 10; } }
}

class OneExpression : Expression
{
	public override string One { get { return "I"; } }

	public override string Four { get { return "IV"; } }

	public override string Five { get { return "V"; } }

	public override string Nine { get { return "IX"; } }

	public override int Weight { get { return 1; } }
}
class Client {
	public static int parse(string number) {
		Context context = new Context(number);
		List<Expression> tree = new ArrayList<Expression>();
		tree.add(new ThousandExpression());
		tree.add(new HundredExpression());
		tree.add(new TenExpression());
		tree.add(new OneExpression());

		for (Expression exp : tree)
			exp.interpret(context);

		return context.getValue();
	}
}

class Context {
	private string query;
	private int value;

	public Context(string input) {
		query = input;
	}

	public string getQuery() {
		return query;
	}

	public void setQuery(string value) {
		this.query = value;
	}

	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}

	public void addToValue(int delta) {
		this.value += delta;
	}
}

abstract class Expression {
    public void interpret(Context context) {
		if (context.getQuery().length() == 0)
			return;

		if (context.getQuery().startsWith(getNine())) {
			context.addToValue(9 * getWeight());
			context.setQuery(context.getQuery().substring(getNine().length()));
		} else if (context.getQuery().startsWith(getFour())) {
			context.addToValue(4 * getWeight());
			context.setQuery(context.getQuery().substring(getFour().length()));
		} else if (context.getQuery().startsWith(getFive())) {
			context.addToValue(5 * getWeight());
			context.setQuery(context.getQuery().substring(getFive().length()));
		}

		while (context.getQuery().startsWith(getOne())) {
			context.addToValue(getWeight());
			context.setQuery(context.getQuery().substring(getOne().length()));
		}
    }

    public virtual string getOne() { 
    	return " ";
    }

    public virtual string getFour() { 
    	return " "; 
    }

    public virtual string getFive() { 
    	return " ";
    }

    public virtual string getNine() { 
    	return " "; 
    }

    public abstract int getWeight();
}

class ThousandExpression extends Expression {
	@Override
	public string getOne() {
		return "M";
	}

	@Override
	public int getWeight() {
		return 1000;
	}
}

class HundredExpression extends Expression {
	@Override
	public string getOne() {
		return "C";
	}

	@Override
	public string getFour() {
		return "CD";
	}

	@Override
	public string getFive() {
		return "D";
	}

	@Override
	public string getNine() {
		return "CM";
	}

	@Override
	public int getWeight() {
		return 100;
	}
}

class TenExpression extends Expression {
	@Override
	public string getOne() {
		return "X";
	}

	@Override
	public string getFour() {
		return "XL";
	}

	@Override
	public string getFive() {
		return "L";
	}

	@Override
	public string getNine() {
		return "XC";
	}

	@Override
	public int getWeight() {
		return 10;
	}
}

class OneExpression extends Expression {
	@Override
	public string getOne() {
		return "I";
	}

	@Override
	public string getFour() {
		return "IV";
	}

	@Override
	public string getFive() {
		return "V";
	}

	@Override
	public string getNine() {
		return "IX";
	}

	@Override
	public int getWeight() {
		return 1;
	}
}
class Client {
	public:
	static int Parse(string number) {
		Context context = new Context(number);
		list<Expression*> tree;
		tree.push_back(new ThousandExpression());
		tree.push_back(new HundredExpression());
		tree.push_back(new TenExpression());
		tree.push_back(new OneExpression());

		for (list<Expression*>::iterator it = tree.begin(); it != tree.end(); ++it)
			it->Interpret(context);

		return context->GetValue();
	}
};

class Context {
	private:
	string query;
	int value;

	public:
	Context(string input) {
		query = input;
		value = 0;
	}

	string GetQuery() {
		return query;
	}

	void SetQuery(string value) {
		this->query = value;
	}

	int GetValue() {
		return value;
	}

	void SetValue(int value) {
		this->value = value;
	}

	void AddToValue(int delta) {
		this->value += delta;
	}
};

class Expression {
    public:
    void Interpret(Context* context) {
		if (context->GetQuery().length() == 0)
			return;

		if (context->GetQuery().find(GetNine()) == 0) {
			context->AddToValue(9 * GetWeight());
			context->SetQuery(context->GetQuery().substr(GetNine().length()));
		} else if (context->GetQuery().find(GetFour()) == 0) {
			context->AddToValue(4 * GetWeight());
			context->SetQuery(context->GetQuery().substr(GetFour().length()));
		} else if (context->GetQuery().find(GetFive()) == 0) {
			context->AddToValue(5 * GetWeight());
			context->SetQuery(context->GetQuery().substr(GetFive().length()));
		}

		while (context->GetQuery().find(GetOne()) == 0) {
			context->AddToValue(GetWeight());
			context->SetQuery(context->GetQuery().substr(GetOne().length()));
		}
    }

    virtual string GetOne() { 
    	return " ";
    }

    virtual string GetFour() { 
    	return " "; 
    }

    virtual string GetFive() { 
    	return " ";
    }

    virtual string GetNine() { 
    	return " "; 
    }

    virtual int GetWeight() = 0;
};

class ThousandExpression : public Expression {
	public:
	string GetOne() {
		return "M";
	}

	int GetWeight() {
		return 1000;
	}
};

class HundredExpression : public Expression {
	public:
	string GetOne() {
		return "C";
	}

	string GetFour() {
		return "CD";
	}

	string GetFive() {
		return "D";
	}

	string GetNine() {
		return "CM";
	}

	int GetWeight() {
		return 100;
	}
};

class TenExpression : public Expression {
	public:
	string GetOne() {
		return "X";
	}

	string GetFour() {
		return "XL";
	}

	string GetFive() {
		return "L";
	}

	string GetNine() {
		return "XC";
	}

	int GetWeight() {
		return 10;
	}
};

class OneExpression : public Expression {
	public:
	string GetOne() {
		return "I";
	}

	string GetFour() {
		return "IV";
	}

	string GetFive() {
		return "V";
	}

	string GetNine() {
		return "IX";
	}

	int GetWeight() {
		return 1;
	}
};

Practical considerations

  • Basic idea: A class for each defined symbol
  • The structure of the syntax tree is given by the composite pattern
  • Compilers are usually not that simple, since they are more fine-grained
  • Simple languages like SQL are, however, perfect suitable
  • In practice the client is called the engine and invoked from a real client
  • The client usually already contains some logic

Literature

  • Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1995). Design Patterns: Elements of Reusable Object Oriented Software.
  • Freeman, Eric; Freeman, Elisabeth; Kathy, Sierra; Bert, Bates (2004). Head First Design Patterns.
  • Buschmann, Frank; Meunier, Regine; Rohnert, Hans; Sommerlad, Peter (1996). Pattern-Oriented Software Architecture, Volume 1: A System of Patterns.
  • Fowler, Martin (2002). Patterns of Enterprise Application Architecture.