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.
Design for reuse (see Template design pattern)
Composite design pattern
Composite pattern
Archetype
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 (a.k.a. DAO or DTO, which stands for Data Transfer Object) rules the way of accessing data in databases. For example, greenDAO is devoted to the Android system to manage embedded databases. In common Java, Java Persistence API (JPA) is a standardized persistence framework in the common Java Virtual Machine (JVM); JPA itself encapsulates the Java DataBase Connectivty (JDBC) standard.

Archetype (Java)
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&);
        };
    }
    …
}
Façade design pattern
Archetype (Java)
Factory design pattern
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
Observer design pattern
Archetype
Example (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 {
    …
    private final java.beans.PropertyChangeSupport _propertySupport = new java.beans.PropertyChangeSupport(this);
    public Run_indicator(Run_indicator_client programmable_thermostat) throws Statechart_exception {
        …
        _propertySupport.addPropertyChangeListener(programmable_thermostat);
    }
    public void off() throws Statechart_exception {
        _Run_indicator.fires(_Something_on, _Everything_off);
        _Run_indicator.run_to_completion();
        String status = _Run_indicator.current_state();
        _propertySupport.firePropertyChange("Run indicator status", _status, status);
        _status = status;
    }
    …    
}
Singleton design pattern
Archetype (Java)
Archetype (C++)
class Global_exception_management { // No instance required!
public:
    static void my_unexpected();
    static void my_terminate();
};
Template design pattern
Archetype

Case study (requirements)

Requirements, first part

Requirements, first part

Requirements, second part

Requirements, second part

Case study (analysis)

Analysis model sample

Analysis model sample

Case study (design for reuse)

Bad design 1

Bad design (1/2)

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