Design patterns


Creative Commons License
This -Design patterns- tutorial is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License
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 method 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 model

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)
Composite design pattern

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

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 transformations (filering, aggregating, sorting…). No complex processing (billing, 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 design pattern

Archetype (Java)

Session Beans in the Enterprise JavaBeans™ (EJB) technology

Here

Factory design pattern

A factory object (e.g., car factory) generates instances a given type, say “Car” from (utility) class methods 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

There

Observer design pattern

Archetype (Java)

Here

Service activator design pattern

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

State design pattern

Components manage their internal state so that they react to requests (a.k.a. “events”, “messages”) in different ways depending upon their current state.

Archetype (Java)

Here

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)

Requirements, first part

Requirements, second part

Case study (analysis)

Analysis model sample

Case study (design for reuse)

Bad design (1/2)

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 in Activity card computation.

Good design (inheritance)

Good design (inheritance)

Good design (composition)

Good design (composition)

Case study (design with reuse)

Design model sample v. 1

Programming model sample (reuse through composition)

Design model sample v. 2

Programming model sample (reuse through multiple inheritance)

Implementation in C++

template<typename T> class Activity_card_computation {
    ...
    protected:
        std::vector<T> _implementation;
    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 in Java

abstract public class Activity_card_computation<T extends Number> extends Number {
    ...
    protected java.util.Activity_card_computation<Number> _implementation;
    ... // 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();
    }
    ...
}
MVC

MVC comes from the Smalltalk programming language, which natively 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 paradigmes are born from MVC.

Archetype (MVC within the 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.

Model (analysis)

Model (design)

Exercise: here