Design patterns



Preamble

This tutorial is a concise overview of Design patterns. The purpose of this tutorial is to show that the two key notions behind OO programming are: design with reuse and design for reuse. To that extent, Design patterns is an informal software design methodology that singles out code architectural styles (a.k.a. “patterns”). Design patterns may preexist in software libraries and, therefore, they are reused “as is” (design with reuse) with the least possible adaptations. On the other hand, new code has to be organized by following schemes promoted by Design patterns (design for reuse).

Headlines
Software quality

Economical quality features (direct impact)

Social quality features (direct impact)

Other key quality features

Design with reuse

Design with reuse encompasses the search, the selection and the controlled integration of trusted software pieces with cost-effectiveness and time-effectiveness (productivity...). The reuse process is a suite of three key phases, namely analysis, design, and programming as it follows.

Analysis

As shown above, white rectangles are “business objects” coming from business analysis.

Design model

Design

At design time, business objects are assigned to library components (gray rectangles) in order to elaborate on how business services may rely on technical functions in these components.

Programming model

Programming

At programming time, glue code allows the effective connection. Often, library components cannot be reused “as is”. Dots in gray rectangles mimic such an adaptation so that library components become fully integrable.

Software architecture with UML

Design for reuse (see Template design pattern)
Adapter design pattern

Adapters deals with unanticipated situations. Design of adapters occurs through extensions like inheritance, aggregation, or more simply wrapping.

Archetype (C++)

#include <string>
#include <variant> // https://en.cppreference.com/w/cpp/utility/variant

class Say_hello {
public:
	enum class Dialect : unsigned { American = 1, English = 44, French = 33 };
	static std::string Hello(Dialect = Dialect::American);
};

union Adaptation; // Replace with 'std::variant'

class Say_hello_adapter {
public:
	enum class Dialect : unsigned { Danish = 45 };
	static std::string Hello(Adaptation);
};

union Adaptation {
	Say_hello::Dialect before;
	Say_hello_adapter::Dialect after;
};
#include <iostream>

#include "Adapter.h"

std::string Say_hello::Hello(Dialect dialect) {
	switch (dialect) {
	case Say_hello::Dialect::American: return "Hi";
	case Say_hello::Dialect::English: return "Hello";
	case Say_hello::Dialect::French: return "Salut";
	default: throw "Unknown";
	}
}
std::string Say_hello_adapter::Hello(Adaptation adaptation) {
	try {
		Say_hello::Hello(adaptation.before); // Reuse...
	}
	catch (const char* exception) {
		switch (adaptation.after) {
		case Say_hello_adapter::Dialect::Danish: return "Hej";
		default: throw "Unknown";
		}
	}
}
int main() {
	std::cout << Say_hello::Hello() << std::endl;
	Adaptation adaptation;
	adaptation.after = Say_hello_adapter::Dialect::Danish;
	std::cout << Say_hello_adapter::Hello(adaptation) << std::endl;

	return 0;
}

Exercise: replace union by type-safe std::variant

Composite design pattern

Software composites delegate or forward business operations to child components. Children may themselves be composites or not (leaf components).

Archetype

UML aggregation (white diamond) and composition (black diamond) relationships (C++ )

template<typename T> class Role : public set<T*, less<T*> > {
template<typename T> class Composite : public Role<T> { …
template<typename T> class Component : public Role<T> { …

template<typename Whole, typename Part> class Aggregation;
template<typename Whole, typename Part> class _Aggregation {
    friend class Aggregation<Whole, Part>;
    …
template<typename Whole, typename Part> class Aggregation {
private:
    static map<string, _Aggregation<Whole, Part>, less<string> > _aggregation;
    string _name;
    …

class Network_element {
};

class Manager;

class Agent {
public:
    virtual Role<Manager> manager() const = 0;
    virtual Composite<Network_element> managed_objects() const = 0;
};

class Manager {
public:
    virtual Role<Agent> agent() const = 0;
    virtual Role<Network_element> accessed_objects() const = 0;
};
Data Access Object -DAO- design pattern

The Data Access Object design pattern delivers raw data while hiding the technical mechanism of accessing data, more often, in databases. For example, greenDAO is devoted to the Android system to manage embedded databases. In Java 8, Java Persistence API -JPA- is a standardized persistence framework within the Java Virtual Machine -JVM-; JPA itself encapsulates Java DataBase Connectivty -JDBC-: the underlying mechanism based on varied database drivers to effectively get the data.

Archetype (Java)

Entity Beans in the Enterprise JavaBeans™ -EJB- technology or Plain Old Data Objects -POJOs- in Hibernate

Here

Archetype (C++)

namespace New_York_City_Penitentiary {
    inline namespace DAOs {
        class Criminal_case { ... };
        …
    }
    namespace Nominal {
        class Prisoner {
            const std::string _prison_file_number;
            std::string _given_name;
            std::string _surname;
            std::tm _date_of_birth;
            std::string _place_of_birth;
            std::tm _date_of_incarceration;
            const Criminal_case * _incarceration_main;
        public:
            explicit Prisoner(const std::string&);
            bool operator<(const Prisoner&) const; // For 'std::set'
            std::string get_prison_file_number() const;
            std::string get_given_name() const;
            void set_given_name(const std::string&);
            const std::string& get_surname() const;
            void set_surname(const std::string&);
            std::tm get_date_of_birth() const;
            void set_date_of_birth(const std::tm&);
            std::string get_place_of_birth() const;
            void set_place_of_birth(const std::string&);
            std::tm get_date_of_incarceration() const;
            void set_date_of_incarceration(const std::tm&);
            const Criminal_case* get_incarceration_main() const;
            void set_incarceration_main(const Criminal_case *);
            std::string toString();
        };
        class Prisoner_hash { // For 'std::unordered_set'
        public:
            std::size_t operator()(Prisoner const&);
        };
    }
    …
}
Data Transfer Object -DTO- design pattern

The Data Transfer Object design pattern is dedicated to the delivery of primitive data from DAOs with lightweight transformation (filtering, aggregating, sorting…). No complex processing (statistics, analytics…) occurs DTOs naturally play the role of façade objects for DAOs.

Archetype (Java)

Stateless Session Beans in the Enterprise JavaBeans™ -EJB- technology

Here

Façade & Resource injection design patterns

Archetype (Java)

Session Beans in the Enterprise JavaBeans™ -EJB- technology acts as façade objects between clients and Entity Beans.

Here

Archetype (C++ )

#include <algorithm> // 'std::any_of'
#include <array> 
#include <initializer_list>

template<typename T, unsigned capacity = 1000> class Pool {
private:
	std::array<T, capacity> _buffer; // Recycling occurs here...
public:
	typedef T CONTENT; // For convenience...
	bool allocate(const T& x) {
		return std::any_of(_buffer.begin(), _buffer.end(), [&x](T& t) -> bool {
			if (t.is_null()) {
				t = x;
				return true; // 'break'
			}
			else return false; // 'continue'
			});
	}
	bool desallocate(const T& x) {
		return std::any_of(_buffer.begin(), _buffer.end(), [&x](T& t) -> bool {
			if (t == x) {
				t.nullify();
				return true; // 'break'
			}
			else return false; // 'continue'
			});
	}
};

template<typename T = char> class Facade { // Pure functional object...
private:
	Pool<T>& _pool;
public: // Resource injection:
	Facade(Pool<T>& pool) : _pool(pool) {}
	// CRUD
	void Create(std::initializer_list<T>&& list) {
		for (auto& element : list)
			_pool.allocate(element);
	}
	// Read...
	// Update...
	void Delete(std::initializer_list<T>&& list) {
		for (auto& element : list)
			_pool.desallocate(element);
	}
};
class Object {
private:
	char _data = '\0';
public:
	Object(char = '\0');
	bool operator==(const Object&) const; 
	// const Object& operator=(const Object&); // Mandatory for complex embedded data...
	bool is_null() const;
	void nullify();
};
Object::Object(char data) : _data(data) {}
bool Object::operator==(const Object& object) const {return _data == object._data;}
bool Object::is_null() const { return _data == '\0'; }
void Object::nullify() { _data = '\0'; }

int main() {
	Pool<Object> pool;
	// Resource injection design pattern, i.e., 'pool' is injected from outside (and may shared by other 'Facade' objects):
	Facade<decltype(pool)::CONTENT> facade(pool); // <=> 'Facade<Object> facade(pool);'
	Object o1('1'), o2('2'), o3('3');
	facade.Create({ o1, o2, o3 });
	facade.Delete({ o1 });

	return 0;
}
Factory design pattern

A factory object (e.g., a book as an intellectual production) generates instances of a given type, say Book copy from (utility) class methods (e.g., Create copy(): Book copy) in general instead of using a new operator in particular.

Archetype (C++)

// '.h' file:
template<typename O, typename P> class Factory {
public:
    static O GetInstance(const P& p) {
        O o(p);
        return o;
    } // Deletion of 'o'...
    static O GetClone(O& o) { // Overloads raise combinatory problems in case of multiple anonymous types...
        // Change 'o' in some way...
        O _o = o; // 'O _o(o);'
        return _o;
    } // Deletion of '_o'...
    static O GetClone(const O& o) { // Overloads raise combinatory problems in case of multiple anonymous types...
        // Change 'o' in some way...
        O _o = o; // 'O _o(o);'
        return _o;
    } // Deletion of '_o'...
    static O GetClone(O&& o) {
        // Change 'o' in some way...
        O _o(std::forward<O>(o)); // Forwards lvalue as either lvalue or as rvalue, depending on 'O'...
        return _o;
    } // Deletion of '_o'...
};

class Person : public Factory<Person, std::string> { … };

// '.cpp' file:
Person someone;
someone = Person::GetInstance("someone"); // Move assignment

Archetype (Java) here

Observer design pattern

Archetype (Java)

JavaBeans™ Component Model (tutorial)

Run indicator (as part of a programmable thermostat) in JavaBeans

public interface Run_indicator_client extends java.beans.PropertyChangeListener {…}

public class Programmable_thermostat extends … implements Run_indicator_client, … {…}

public class Run_indicator implements java.io.Serializable {
    …
    public Run_indicator(Run_indicator_client programmable_thermostat) throws Statechart_exception {
        …
        propertySupport = new java.beans.PropertyChangeSupport(this);
        propertySupport.addPropertyChangeListener(programmable_thermostat);
    }
    public void off() throws Statechart_exception {
        _Run_indicator.fires(_Something_on,_Everything_off);
        _Run_indicator.run_to_completion();
        _setStatus(_Run_indicator.current_state());
    }
    private void _setStatus(String newValue) {
        String oldValue = status;
        status = newValue;
        propertySupport.firePropertyChange("Run indicator status",oldValue,newValue);
    }
    …
}

Archetype C++

Mediator design pattern

Intent

Objects use a proxy to communicate. To lower coupling, objects ignore interfaces of objects with whom they exchange information.

Archetype Java

public class Main {

    public static void main(String[] args) {
        Communicator callee = new Communicator("callee");
        Communicator caller = new Communicator("caller");

        java.util.EventObject how_are_you_doing_ = new java.util.EventObject(caller) {
            @Override
            public String toString() {
                return "How are you doing?";
            }
        };
        java.util.EventObject fine_and_you_ = new java.util.EventObject(callee) {
            @Override
            public String toString() {
                return "Fine and you?";
            }
        };
        java.util.EventObject not_too_bad_ = new java.util.EventObject(caller) {
            @Override
            public String toString() {
                return "Not too bad...";
            }
        };

        Mediator.Subscribe(callee, how_are_you_doing_);
        Mediator.Subscribe(caller, fine_and_you_);
        Mediator.Subscribe(callee, not_too_bad_);

        Mediator.Occur(how_are_you_doing_);
        Mediator.Occur(fine_and_you_);
        Mediator.Occur(not_too_bad_);
    }
}
interface Event_handler { // Inspiration from https://docs.oracle.com/javase/tutorial/uiswing/events/actionlistener.html
    void occur(java.util.EventObject event);
}

public class Mediator {

    // Possible extension is multiple event handlers:
    private static final java.util.Map<java.util.EventObject, Event_handler> _Communication = new java.util.HashMap<>();

    public static void Occur(java.util.EventObject event) {
        Event_handler callee = _Communication.get(event);
        if (callee != null) 
            callee.occur(event);
    }

    public static void Subscribe(Communicator callee, java.util.EventObject event) {
        _Communication.put(event, (Event_handler) (java.util.EventObject event_) -> {
            callee.handle(event_);
        });
    }
}

Exercice (C++)

Service activator design pattern

Intent

Service consumption may stumble over issues like unavailability, quality of returned data (nature, complexity…), changes in pricing policy, etc. Service activator is a uniform entry point hiding the routing to various effective competing services. Choice of consumed service may then operate from concerns about failure, performance, security…

Archetype (Java )

public class Currency_conversion_service_activator {

    public static void main(String[] args) {
        try {
// Single currency conversion, for instance, from US $1000 to €:
            System.out.println("OpenExchangeRates.org: " + Double.toString(OpenExchangeRates.Convert(Currencies.USD, Currencies.EUR, 1000.)) + '\u20A0');
            System.out.println("OpenExchangeRates.org: " + Double.toString(OpenExchangeRates.Convert2(Currencies.USD, Currencies.EUR, 1000.)) + '\u20A0');
// Paying clients only, i.e., a Java exception is raised because HTTPS access requires payment: 
            System.out.println(Double.toString(OpenExchangeRates.Convert3(Currencies.USD, Currencies.EUR, 1000.)) + '\u20A0');
        } catch (java.net.MalformedURLException mfue) { // 'MalformedURLException' inherits from 'IOException'
            System.err.println(java.net.MalformedURLException.class.getSimpleName() + ": " + mfue.getMessage());
        } catch (java.io.IOException ioe) {
            System.err.println("Call 'OpenExchangeRates.org' through HTTPS is subject to payment, so it fails: " + ioe.getMessage());
            try {
                System.out.println("Fixer.io: " + Double.toString(Fixer.Convert(Currencies.USD, Currencies.EUR, 1000.)) + '\u20A0');
            } catch (java.io.IOException ioe_) {
                System.err.println("Call 'Fixer.io' fails as well: " + ioe_.getMessage());
            }
        }
    }
}

Archetype (Java 9 here…)

Singleton design pattern

Archetype (C++)

class Global_exception_management { // No instance required!
public:
    static void my_unexpected();
    static void my_terminate();
};

Archetype (Java)

Singleton Session Beans in the Enterprise JavaBeans™ -EJB- technology here

Exercise (Java)

Bridge design pattern

Archetype (Java)

java.util.Comparator<T> interface here

Exercise

Chain of responsibility design pattern

Intent

Enhancement of flexibility and uniformity in a chain of processing (checking typically) steps.

Archetype (Java )

public interface Disability {
    default public String attestation() {
        return "Bla Bla Bla...";
    }
    public enum Equipment {
        Crutch, None, Wheelchair // Etc.
    }
    default Equipment equipment() {
        return Equipment.None;
    }
    default int escort() {
        return 0; // Number of support people
    }
}
…
@java.lang.FunctionalInterface
public interface Responsibility_link { // Variable number of responsibility links in chain:
    boolean run(final Disability disability, Responsibility_link... followers);
}
…
class Paraplegia implements Disability {
    @Override
    public String attestation() {
        return "Paraplegia";
    }
    @Override
    public Equipment equipment() {
        return Equipment.Wheelchair;
    }
}

public class Chain_of_responsibility {
    public static void main(String[] args) {
        Responsibility_link attestation = (disability, chain_of_responsibility) -> {
            return disability.attestation().equals("Bla Bla Bla...")
                    ? false
                    : chain_of_responsibility.length == 0
                            ? true
                            // Performance issue (initial primitive array is copied without first element):
                            : chain_of_responsibility[0].run(disability, java.util.Arrays.copyOfRange(chain_of_responsibility, 1, chain_of_responsibility.length));
        };
        Responsibility_link equipment = (disability, chain_of_responsibility) -> {
            return disability.equipment() == Disability.Equipment.None
                    ? false
                    : chain_of_responsibility.length == 0
                            ? true
                            : chain_of_responsibility[0].run(disability, java.util.Arrays.copyOfRange(chain_of_responsibility, 1, chain_of_responsibility.length));
        };
        Responsibility_link escort = (disability, chain_of_responsibility) -> {
            return disability.escort() > 1 ? false
                    : chain_of_responsibility.length == 0
                            ? true
                            : chain_of_responsibility[0].run(disability, java.util.Arrays.copyOfRange(chain_of_responsibility, 1, chain_of_responsibility.length));
        };
        System.out.println("Event access? " + attestation.run(new Paraplegia(), equipment, escort));
    }
}

Exercise

Translate this Java code in C++ with extensions as follows.

  1. Extend code to deal with disability nature between “mental”, “physical” (default), or both
  2. Insert a new Responsibility_link object in chain_of_responsibility (second position), which controls disability nature according to this rule: access to an event raises a problem for disabled persons with both “mental” and “physical” issues
  3. Create “Alzheimer” disability for which only one escort is necessary; test its accessibility to an event
State design pattern

Intent

Components autonomously manage their internal (complex) state so that they react to requests (a.k.a. “events”, “messages”) in different ways depending upon their current state. The complex nature of their internal state relies on substates, which may be concomittant, nested…

Archetype (Java) here

Flyweight design pattern

Intent

Memory sobriety prompts us to divide the state of an object between its intrinsic state (immutable, set up at construction time) and its extrinsic state (mutable through operations in particular).

Archetype (UML)


Exercise

Implement this UML model in C++ or Java with enhancements as follows.

Template design pattern

Archetype

Graphical User Interface -GUI- libraries provide template classes (shapes, windows…) with common drawing and interaction capabilities. Customization (through inheritance in general) then relies on template completion.

Case study (requirements, first part)

Case study (requirements, second part)

Analysis

Design for reuse ⤳ bad design (1/2)

Design for reuse ⤳ bad design (2/2)

In fact, the two bad designs above do not favor maintainability at the time a third norm calculation would be introduced in the application. To better manage this potential evolution, the two good designs below are based on the Template design pattern. Namely the Activity card computation (generic) class is abstract with two concrete (generic) subclasses, Activity card comp. quad. and Activity card comp. max. that both implement the norm abstract function inherited from Activity card computation.

Good design (inheritance)

Good design (embedding)

Implementation (C++ )

template<typename T> class Activity_card_computation {
    ...
    protected:
        std::vector<T> _implementation; // Embedding...
    public:
        ... // Constructors and destructors here…
        virtual double norm() const = 0;
        double norm_addition(const Activity_card_computation& v) const {
            return (*this +v).norm();
        }
        double norm_subtraction(const Activity_card_computation& v) const {
            return (*this -v).norm();
        }
        double lambda_norm(const double lambda) const {
            return std::fabs(lambda) * norm();
        }
        ...
    };

Implementation (Java )

abstract public class Activity_card_computation<T extends Number> extends Number {
    ...
    protected java.util.Activity_card_computation<Number> _implementation; // Embedding...
    ... // Constructors here…
    abstract public double norm();
    public double norm_addition(final Activity_card_computation<T> v) {
        return this.plus(v).norm();
    }
    public double norm_subtraction(final Activity_card_computation<T> v) {
        return this.minus(v).norm();
    }
    public double lambda_norm(final double lambda) {
        return Math.abs(lambda) * norm();
    }
    ...
}

Design with reuse ⤳ embedding (Java)

Design with reuse ⤳ multiple inheritance (C++)

Exercise

Implement following UML model while adding Livret A here… and Livret de Développement Durable et Solidaire -LDDS- here… Wage rate of Livret A is frozen by French government at a given date. Wage rates of Compte Epargne Logement -CEL-, Plan Epargne Logement -PEL- and Livret de Développement Durable et Solidaire -LDDS- are also frozen and computation formula depends upon wage rate of Livret A.

Model View Controller -MVC-

MVC comes from the Smalltalk programming language, which nativly offers the Model, View, and Controller abstract (i.e., template) classes. Followers were the majority, e.g., CDocument/CView in Visual C++ ver. 1.0 or Swing GUI library in Java.

Event-based programming, service computing and many other paradigms are born from MVC.

Archetype (Web)

Principle

Software quality

Agglomérer dans un même objet données graphiques et données « métier » rend les objets difficilement maintenables.

Avec MVC, les vues, qui sont propres aux applications (voire instables), peuvent changer sans (foncièrement) changer les modèles qui sont (plus stables) partagés par les applications (réutilisation).

Finalement, MVC encourage un découpage logique des attributions lui même engendrant une meilleure isolation des objets aux fautes (fiabilité). A minima, une bonne localisation du code (high cohesion) et des dépendances mieux explicitées (low coupling) favorisent la réparation en cas de bogue.

Analysis

Design

Exercise here