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).
Economical quality features (direct impact)
- Reusability
- Maintainability
- Reliability (correctness)
Social quality features (direct impact)
- Reliability (integrity, safety-critical applications versus error-free applications that maintain corporate identity)
- Scalability (including performance)
- Security
- Ease of use
Other key quality features
- Portability (including interoperability)
- Sustainability (sobriety)
- Etc.
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
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
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
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-safestd::variant
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; };
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&); }; } … }
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 anew
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…
Archetype (Java)
- Observer historically embodies the principles of state change and notification behind the Model-View-Controller -MVC- programming paradigm. One of the Java native supports of Observer are deprecated. Indeed, both the
java.util.Observable
class and thejava.util.Observer
interface aim at being forgotten…- Instead, the Java native supports of Observer in
java.beans
(JavaBeans™ technology) still persist through these two classes:«class»java.util.EventObject
⇧ «class»Two interfaces also play a great role in the Java native supports of Observer injava.util.PropertyChangeEvent
java.beans
:«interface»java.util.EventListener
⇧ «interface»java.beans.PropertyChangeListener
JavaBeans™ Component Model (tutorial)
- Java Specification Request (JSR): JavaBeans™ is fully part of Java SE
- A Java bean is nothing else than a Java class with a special pattern
- Properties are associated with getter and setter methods with special naming rules:
given_name
leads togetGiven_name
(getter) andsetGiven_name
(setter)- Binary storage:
java.io.Serializable
- Event-based programming
- Integrated documentation for easier exchange (reuse) and standardized processing/manipulation in (no longer supported!) dedicated tools like the Beans Development Kit -BDK-
- Inspired by the MVC paradigm
- Invented for composability (“Components are for composition”, Szyperski et al.)
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++ ☛
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++)
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…)
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)
- Create and record single instance through static initialiser here…
- Serve single instance through
Get
static method- Override default constructor visibility: package ⤳
private
Archetype (Java)
java.util.Comparator<T>
interface here…Exercise
- There are hot dishes and cold dishes
- A vegetarian niçoise salad has neither tuna nor anchovy. Besides, a vegan niçoise salad is vegetarian, without eggs. All three variations (normal, vegetarian, and vegan) are cold dishes…
- Create
Dish
class withrealize
member function- Create by means of inheritance
Hot dish
andCold dish
classes- Create by means of inheritance
Vegetable
,Foil vegetable
,Salad vegetable
, andMesclun
classes withprepare
member function- Realizing a niçoise salad partially consists in preparing compound vegetables (mesclun…) as ingredients
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.
- Extend code to deal with disability nature between “mental”, “physical” (default), or both
- Insert a new
Responsibility_link
object inchain_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- Create “Alzheimer” disability for which only one escort is necessary; test its accessibility to an event
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…
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.
- Introduce a pool of
Car
objects; Creation of aFunctional car
object leads to get its associated car in the pool (or create a newCar
object if absent). The new car is added to the pool.- For convenience, introduce a 6-argument constructor for
Functional car
. Note that this approach has limitations ifCar
has many (instance) business attributes.
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.
andActivity card comp. max.
that both implement thenorm
abstract function inherited fromActivity 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.
MVC comes from the Smalltalk programming language, which nativly offers the
Model
,View
, andController
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
Model
: une instance de classe dans laquelle des données métier sont stockées, des états sont maintenus, des constantes sont définies… sans préjuger de la façon dont tout ou partie de ces éléments sont affichés.View
: une ou plusieurs instances de classes graphiques (widgets) qui incarne(nt) une représentation sur écran « du » modèle. Par nature, ces objets ont des propriétés géométriques (position, orientation…) et plus généralement visuelles (couleur, 2D/3D…). «View
est le pendant visuel métaphorique deModel
».Controller
: une instance de classe chargée d'écouter des événements « tiers » (interaction) en relation avec le système de fenêtrage (clic souris, entrée clavier…). Le langage de programmation offre un système d'abonnement à tout type de notification captée par le langage au niveau du système d'exploitation.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…