├── .gitignore ├── SingletonPattern ├── Main.cpp ├── doc │ └── singleton_diagram.txt ├── README.md └── Singleton.hpp ├── AbstractFactoryPattern ├── Factory.hpp ├── Main.cpp ├── doc │ └── abstract_factory_diagram.txt ├── Colors.hpp ├── Shapes.hpp ├── AbstractFactory.hpp ├── ColorFactory.hpp ├── ShapeFactory.hpp └── README.md ├── FactoryPattern ├── doc │ └── factory_diagram.txt ├── Factory.hpp ├── Main.cpp ├── Animals.hpp ├── README.md └── AnimalFactory.hpp ├── .travis.yml ├── ObserverPattern ├── doc │ └── observer_diagram.txt ├── Main.cpp ├── README.md └── Subject.hpp ├── CMakeLists.txt ├── StrategyPrototypePattern ├── Main.cpp ├── doc │ └── strategy_diagram.txt ├── Algorithms.hpp ├── README.md └── Container.hpp ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | build/ -------------------------------------------------------------------------------- /SingletonPattern/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "Singleton.hpp" 2 | 3 | int main() 4 | { 5 | Singleton::getInstance().print(); 6 | return 0; 7 | } -------------------------------------------------------------------------------- /SingletonPattern/doc/singleton_diagram.txt: -------------------------------------------------------------------------------- 1 | @startuml 2 | class Singleton { 3 | - Singleton singleton 4 | - Singleton() 5 | + {static} Singleton getInstance() 6 | } 7 | @enduml -------------------------------------------------------------------------------- /AbstractFactoryPattern/Factory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace factory 7 | { 8 | 9 | template 10 | using Factory = std::function; 11 | 12 | } -------------------------------------------------------------------------------- /FactoryPattern/doc/factory_diagram.txt: -------------------------------------------------------------------------------- 1 | @startuml 2 | actor You 3 | 4 | You -> AnimalFactory : Create DuckFactory 5 | AnimalFactory --> You : DuckFactory 6 | You -> DuckFactory : Create Duck 7 | DuckFactory -> Duck : Create Duck 8 | Duck --> DuckFactory : New Duck 9 | DuckFactory --> You : New Duck 10 | @enduml -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | compiler: 4 | - gcc 5 | 6 | addons: 7 | apt: 8 | sources: 9 | - ubuntu-toolchain-r-test 10 | packages: 11 | - g++-7 12 | install: 13 | - "[ $CXX = g++ ] && export CXX=g++-7 || true" 14 | 15 | before_script: 16 | - mkdir build 17 | - cd build 18 | - cmake ../ 19 | 20 | script: make 21 | -------------------------------------------------------------------------------- /AbstractFactoryPattern/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "AbstractFactory.hpp" 2 | #include "ShapeFactory.hpp" 3 | 4 | int main() 5 | { 6 | using namespace abstractfactory; 7 | using namespace shapefactory; 8 | 9 | auto rectFactory = factoryProducer(FactoryTag::shape)(ShapeTag::rectangle); 10 | 11 | auto shape = rectFactory(); 12 | 13 | shape->draw(); 14 | 15 | return 0; 16 | } -------------------------------------------------------------------------------- /FactoryPattern/Factory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace factory 7 | { 8 | 9 | /** 10 | * A template for a factory, which is just a std::function. 11 | * See AnimalFactory.hpp for example usage. 12 | */ 13 | template 14 | using Factory = std::function; 15 | 16 | } -------------------------------------------------------------------------------- /ObserverPattern/doc/observer_diagram.txt: -------------------------------------------------------------------------------- 1 | @startuml 2 | class Subject { 3 | - std::list> observers 4 | - StateType state 5 | - void notifyObservers() 6 | + Subject() 7 | + void setState(const StateType& state) 8 | + StateType getState() 9 | + void attach(std::function, const std::string& key) 10 | + void detach(const std::string& key) 11 | } 12 | @enduml -------------------------------------------------------------------------------- /AbstractFactoryPattern/doc/abstract_factory_diagram.txt: -------------------------------------------------------------------------------- 1 | @startuml 2 | actor You 3 | 4 | You -> AbstractFactory : factoryProducer(shape) 5 | AbstractFactory --> You : shapeFactoryProducer 6 | You -> ShapeFactory : shapeFactoryProducer(rectangle) 7 | ShapeFactory --> You : RectangleFactory 8 | You -> RectangleFactory : RectangleFactory operator() 9 | RectangleFactory -> Rectangle : Create Rectangle 10 | Rectangle --> RectangleFactory : New Rectangle 11 | RectangleFactory --> You : New Rectangle 12 | @enduml -------------------------------------------------------------------------------- /AbstractFactoryPattern/Colors.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Color { 6 | public: 7 | virtual void draw() = 0; 8 | virtual ~Color() {} 9 | }; 10 | 11 | class Green : public Color { 12 | public: 13 | void draw() override 14 | { 15 | std::cout << "Drawing Green." << std::endl; 16 | } 17 | }; 18 | 19 | class Blue : public Color { 20 | public: 21 | void draw() override 22 | { 23 | std::cout << "Drawing Blue." << std::endl; 24 | } 25 | }; 26 | 27 | class Red : public Color { 28 | public: 29 | void draw() override 30 | { 31 | std::cout << "Drawing Red." << std::endl; 32 | } 33 | }; -------------------------------------------------------------------------------- /AbstractFactoryPattern/Shapes.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Shape { 6 | public: 7 | virtual void draw() = 0; 8 | virtual ~Shape() {}; 9 | }; 10 | 11 | class Rectangle : public Shape { 12 | public: 13 | void draw() override 14 | { 15 | std::cout << "Drawing rectangle." << std::endl; 16 | } 17 | }; 18 | 19 | class Square : public Shape { 20 | public: 21 | void draw() override 22 | { 23 | std::cout << "Drawing Square." << std::endl; 24 | } 25 | }; 26 | 27 | class Circle : public Shape { 28 | public: 29 | void draw() override 30 | { 31 | std::cout << "Drawing Circle." << std::endl; 32 | } 33 | }; -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | set (CMAKE_CXX_STANDARD 17) 3 | project (ModernDesignPatterns) 4 | 5 | set(PATTERNS 6 | StrategyPrototypePattern 7 | ObserverPattern 8 | SingletonPattern 9 | FactoryPattern 10 | AbstractFactoryPattern 11 | ) 12 | 13 | macro(add_pattern PATTERN) 14 | include_directories(${PATTERN}) 15 | file(GLOB_RECURSE PATTERN_FILES "${PATTERN}/*.cpp") 16 | add_executable(${PATTERN} ${PATTERN_FILES}) 17 | endmacro(add_pattern) 18 | 19 | foreach(PATTERN IN LISTS PATTERNS) 20 | message(STATUS "Compiling Pattern: ${PATTERN}") 21 | add_pattern(${PATTERN}) 22 | endforeach(PATTERN) -------------------------------------------------------------------------------- /SingletonPattern/README.md: -------------------------------------------------------------------------------- 1 | # Singleton Pattern 2 | 3 | For when you for some reason need a singleton. **I don't recommend using this but added it for completeness.** 4 | This is thread-safe due to [Magic Statics](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2660.htm). 5 | 6 | Supported from VC++2015 and GCC C++11. 7 | 8 | One suggested alternative to a singleton is to throw an exception when trying to instantiate x objects. 9 | 10 | ## UML 11 | 12 | ![alternative text](http://www.plantuml.com/plantuml/proxy?src=https://raw.githubusercontent.com/jonathan-daniel/ModernDesignPatterns/master/SingletonPattern/doc/singleton_diagram.txt&fmt=svg) -------------------------------------------------------------------------------- /AbstractFactoryPattern/AbstractFactory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ShapeFactory.hpp" 4 | #include "ColorFactory.hpp" 5 | 6 | namespace abstractfactory 7 | { 8 | 9 | struct FactoryTag 10 | { 11 | struct Shape {} static constexpr shape{}; 12 | struct Color {} static constexpr color{}; 13 | }; 14 | 15 | template 16 | auto factoryProducer(TFactory tag) 17 | { 18 | if constexpr (std::is_same::value) 19 | return shapefactory::shapeFactoryProducer; 20 | else if constexpr (std::is_same::value) 21 | return colorfactory::colorFactoryProducer; 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /StrategyPrototypePattern/Main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Container.hpp" 5 | #include "Algorithms.hpp" 6 | 7 | using namespace std; 8 | 9 | int main() 10 | { 11 | Container someContainer(make_unique()); 12 | someContainer.sort(); 13 | someContainer.setAlgo(make_unique()); 14 | someContainer.sort(); 15 | 16 | Container secondContainer = someContainer; 17 | 18 | secondContainer.sort(); 19 | someContainer.sort(); 20 | 21 | Container thirdContainer(make_unique()); 22 | thirdContainer = secondContainer; 23 | thirdContainer.setAlgo(make_unique()); 24 | thirdContainer.sort(); 25 | return 0; 26 | } -------------------------------------------------------------------------------- /AbstractFactoryPattern/ColorFactory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Factory.hpp" 4 | #include "Colors.hpp" 5 | 6 | #include 7 | 8 | namespace colorfactory 9 | { 10 | 11 | using ColorFactory = factory::Factory>; 12 | 13 | enum class ColorTag {green, red, blue}; 14 | 15 | ColorFactory colorFactoryProducer(ColorTag tag) 16 | { 17 | return [=] 18 | { 19 | if(tag == ColorTag::green) 20 | return std::unique_ptr(new Green()); 21 | else if(tag == ColorTag::blue) 22 | return std::unique_ptr(new Blue()); 23 | else if(tag == ColorTag::red) 24 | return std::unique_ptr(new Red()); 25 | else 26 | return std::unique_ptr(nullptr); 27 | }; 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /StrategyPrototypePattern/doc/strategy_diagram.txt: -------------------------------------------------------------------------------- 1 | @startuml 2 | class Container { 3 | - std::unique_ptr algo 4 | + Container(std::unique_ptr algo) 5 | + void sort() 6 | + void setAlgo(std::unique_ptr algo) 7 | } 8 | 9 | abstract class SortAlgorithm { 10 | + {abstract} void Sort() 11 | + std::unique_ptr clone() 12 | # {abstract} SortAlgorithm* clone_derived() 13 | } 14 | 15 | Container *-- SortAlgorithm 16 | 17 | class QuickSort { 18 | + void sort() 19 | # QuickSort* clone_derived() 20 | } 21 | 22 | class MergeSort { 23 | + void sort() 24 | # MergeSort* clone_derived() 25 | } 26 | 27 | SortAlgorithm <|-- QuickSort 28 | SortAlgorithm <|-- MergeSort 29 | @enduml -------------------------------------------------------------------------------- /AbstractFactoryPattern/ShapeFactory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Factory.hpp" 4 | #include "Shapes.hpp" 5 | 6 | #include 7 | 8 | namespace shapefactory 9 | { 10 | 11 | using ShapeFactory = factory::Factory>; 12 | 13 | enum class ShapeTag {rectangle, circle, square}; 14 | 15 | ShapeFactory shapeFactoryProducer(ShapeTag tag) 16 | { 17 | return [=] 18 | { 19 | if(tag == ShapeTag::rectangle) 20 | return std::unique_ptr(new Rectangle()); 21 | else if(tag == ShapeTag::circle) 22 | return std::unique_ptr(new Circle()); 23 | else if(tag == ShapeTag::square) 24 | return std::unique_ptr(new Square()); 25 | else 26 | return std::unique_ptr(nullptr); 27 | }; 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /SingletonPattern/Singleton.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // for the test print 4 | using namespace std; 5 | 6 | class Singleton { 7 | public: 8 | static Singleton& getInstance() 9 | { 10 | static Singleton instance; 11 | return instance; 12 | } 13 | 14 | // A test function 15 | void print() 16 | { 17 | cout << "test" << endl; 18 | } 19 | private: 20 | Singleton() = default; 21 | ~Singleton() = default; 22 | public: 23 | // Delete the copy and move constructors. 24 | // These should be public as it results in clearer error messages. 25 | Singleton(const Singleton&) = delete; 26 | Singleton& operator=(const Singleton&) = delete; 27 | Singleton(Singleton&&) = delete; 28 | Singleton& operator=(Singleton&&) = delete; 29 | }; -------------------------------------------------------------------------------- /ObserverPattern/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "Subject.hpp" 2 | 3 | #include 4 | 5 | using namespace std; 6 | 7 | struct State { 8 | int data; 9 | }; 10 | 11 | void handleStateChange(const State& newState) 12 | { 13 | cout << "State changed to: " << newState.data << endl; 14 | } 15 | 16 | int main() 17 | { 18 | // Create a new subject with a certain state. 19 | Subject someSubject; 20 | 21 | // Attach the observer, this can be any function. 22 | // Multiple observers can have the same key. 23 | // Or no key at all. 24 | someSubject.attach(handleStateChange, "stateHandle"); 25 | 26 | // Get the state and change it. 27 | State state = someSubject.getState(); 28 | state.data = 1337; 29 | someSubject.setState(state); 30 | 31 | // Detach the observer 32 | someSubject.detach("stateHandle"); 33 | 34 | state.data = 12; 35 | someSubject.setState(state); 36 | 37 | return 0; 38 | } -------------------------------------------------------------------------------- /StrategyPrototypePattern/Algorithms.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | class SortAlgorithm { 9 | public: 10 | virtual void sort() = 0; 11 | virtual ~SortAlgorithm() = default; 12 | 13 | auto clone() const 14 | { 15 | return std::unique_ptr(clone_derived()) ; 16 | } 17 | protected: 18 | virtual SortAlgorithm* clone_derived() const = 0; 19 | }; 20 | 21 | class QuickSort : public SortAlgorithm { 22 | public: 23 | void sort() override { std::cout << "QuickSort" << std::endl; } 24 | protected: 25 | virtual QuickSort* clone_derived() const override 26 | { 27 | return new QuickSort(*this); 28 | } 29 | }; 30 | 31 | class MergeSort : public SortAlgorithm { 32 | public: 33 | void sort() override { std::cout << "MergeSort" << std::endl; } 34 | protected: 35 | virtual MergeSort* clone_derived() const override 36 | { 37 | return new MergeSort(*this); 38 | } 39 | }; -------------------------------------------------------------------------------- /StrategyPrototypePattern/README.md: -------------------------------------------------------------------------------- 1 | # Strategy & Prototype Design Pattern 2 | 3 | With the strategy design pattern a class (in this case `Container`)contains an unique pointer to another object which provides a certain functionality (in this case `SortAlgorithm`), which can dynamically be changed and is easy to extend with new functionalities/algorithms (following the SOLID principles). 4 | 5 | However to prevent inheritance slicing of `SortAlgorithm` subclasses in the `Container` copy constructor the **clone pattern** is also used, so this is actually a combination of two design patterns. 6 | 7 | The clone pattern allows the `Container` class to clone a derived `SortAlgorithm` without losing subclass-specific members: 8 | 9 | ```C++ 10 | Container(const Container& other) : algo(other.algo->clone()) {} 11 | ``` 12 | ## UML 13 | 14 | ![alternative text](http://www.plantuml.com/plantuml/proxy?src=https://raw.githubusercontent.com/jonathan-daniel/ModernDesignPatterns/master/StrategyPrototypePattern/doc/strategy_diagram.txt&fmt=svg) -------------------------------------------------------------------------------- /FactoryPattern/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "Animals.hpp" 2 | #include "AnimalFactory.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | int main() 9 | { 10 | // Create factories to use later 11 | auto animalFactory = animalfactory::animalFactory(); 12 | auto specialDogFactory = animalfactory::animalFactory("special_dog"); 13 | auto mediumFarmFactory = animalfactory::farmFactory(20); 14 | 15 | // Create one duck now 16 | auto duck = animalfactory::animalFactory("duck")(); 17 | 18 | // Create base animal object 19 | auto someAnimal = animalFactory(); 20 | 21 | // Create and downcast special dog to access derived members. 22 | auto specialDog = std::dynamic_pointer_cast( 23 | std::shared_ptr( 24 | specialDogFactory() 25 | ) 26 | ); 27 | 28 | // Test downcast 29 | std::cout << std::to_string(specialDog->getExtraInt()) << std::endl; 30 | 31 | // Create a farm with 20 animals 32 | auto animals = mediumFarmFactory(); 33 | 34 | animals[0].doSomething(); 35 | 36 | return 0; 37 | } -------------------------------------------------------------------------------- /FactoryPattern/Animals.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Animal { 6 | private: 7 | int animalInt; 8 | public: 9 | Animal(int animalInt) : animalInt(animalInt) {} 10 | Animal() : animalInt(12) {} 11 | virtual ~Animal(){} // the base class must have a virtual method. 12 | 13 | virtual void doSomething() 14 | { 15 | std::cout << "animal stuff" << std::endl; 16 | } 17 | }; 18 | 19 | class Dog : public Animal { 20 | private: 21 | int dogInt; 22 | int dogIntExtra = {-1}; 23 | public: 24 | Dog() : Animal(10), dogInt(10) {} 25 | 26 | Dog(bool extraprint) : Animal(10), dogInt(10), dogIntExtra(999) 27 | { 28 | if(extraprint) 29 | std::cout << "Dog stuff with extra stuff" << std::endl; 30 | } 31 | 32 | void doSomething() override 33 | { 34 | std::cout << "dog stuff" << std::endl; 35 | } 36 | 37 | int getExtraInt() 38 | { 39 | return dogIntExtra; 40 | } 41 | }; 42 | 43 | class Duck : public Animal { 44 | private: 45 | int duckInt; 46 | public: 47 | Duck() : Animal(1337), duckInt(1337) {} 48 | 49 | void doSomething() override 50 | { 51 | std::cout << "duck stuff" << std::endl; 52 | } 53 | }; -------------------------------------------------------------------------------- /StrategyPrototypePattern/Container.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Algorithms.hpp" 4 | 5 | #include 6 | #include 7 | 8 | class Container { 9 | private: 10 | std::unique_ptr algo = std::make_unique(); 11 | public: 12 | Container(std::unique_ptr algo) : algo(move(algo)) {} 13 | 14 | void sort() 15 | { 16 | if(algo) 17 | algo->sort(); 18 | else 19 | std::cout << "Sort algorithm not set." << std::endl; 20 | } 21 | 22 | // Use the clone pattern to prevent slicing of derived object. 23 | Container(const Container& other) : algo(other.algo->clone()) {} 24 | 25 | Container& operator=(const Container& other) 26 | { 27 | Container tmp(other); // Re-use copy-constructor. 28 | *this = std::move(tmp); 29 | return *this; 30 | } 31 | 32 | // Set the rvalue constructor and move assignment. 33 | // It is deleted because the copy constructor of unique_ptr is deleted. 34 | Container(Container&& other) = default; 35 | Container& operator=(Container&& other) = default; 36 | 37 | void setAlgo(std::unique_ptr algo) 38 | { 39 | this->algo = move(algo); 40 | } 41 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jonathan Daniel (emailjonathandaniel.website) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ObserverPattern/README.md: -------------------------------------------------------------------------------- 1 | # Observer Design Pattern 2 | 3 | The observer pattern allows objects to listen for state changes in a certain `Subject`. 4 | 5 | This implementation makes no assumptions and is completely decoupled from the observers. This is done by setting the state as a template typename of the class and attaching only the function that handles the changes. 6 | 7 | This function must be of format `void f(const StateType&)` where StateType is the same as the one specified to the `Subject` class. 8 | 9 | An example: 10 | 11 | ```C++ 12 | struct State { int data; }; 13 | 14 | void handleStateChange(const State& newState) 15 | { 16 | cout << "State changed to: " << newState.data << endl; 17 | } 18 | 19 | int main() 20 | { 21 | // Create a new subject with a certain state. 22 | Subject someSubject; 23 | 24 | // Attach the observer, this can be any function. 25 | someSubject.attach(handleStateChange); 26 | 27 | // Get the state and change it. 28 | State state = someSubject.getState(); 29 | state.data = 1337; 30 | someSubject.setState(state); 31 | return 0; 32 | } 33 | ``` 34 | 35 | ## UML 36 | 37 | ![alternative text](http://www.plantuml.com/plantuml/proxy?src=https://raw.githubusercontent.com/jonathan-daniel/ModernDesignPatterns/master/ObserverPattern/doc/observer_diagram.txt&fmt=svg) -------------------------------------------------------------------------------- /AbstractFactoryPattern/README.md: -------------------------------------------------------------------------------- 1 | # Abstract Factory Pattern 2 | 3 | This is an abstract factory pattern. 4 | 5 | It uses `std::is_same` with `if constexpr` (C++17) to deduct the return type of `abstractfactory::factoryProducer` at compile-time. 6 | 7 | ## Example usage 8 | 9 | ```C++ 10 | #include "AbstractFactory.hpp" 11 | #include "ShapeFactory.hpp" 12 | 13 | int main() 14 | { 15 | using namespace abstractfactory; 16 | using namespace shapefactory; 17 | 18 | auto rectFactory = factoryProducer(FactoryTag::shape)(ShapeTag::rectangle); 19 | 20 | auto shape = rectFactory(); 21 | 22 | shape->draw(); 23 | 24 | return 0; 25 | } 26 | ``` 27 | 28 | ## Sequence Diagram 29 | 30 | ![alternative text](http://www.plantuml.com/plantuml/proxy?src=https://raw.githubusercontent.com/jonathan-daniel/ModernDesignPatterns/master/AbstractFactoryPattern/doc/abstract_factory_diagram.txt&fmt=svg) 31 | 32 | In this diagram you can see that the AbstractFactory module (`AbstractFactory.hpp`) returns a function (in this case `shapeFactoryProducer`) that can produce a factory based on a parameter (a tag). This factory (`RectangleFactory`) can later be called to generate objects when needed. 33 | 34 | The behaviour and tags can be customised to generate anything at any time, by using `unique_ptr` no data is copied which is best for performance. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Modern Design Patterns 2 | 3 | **Common Design Patterns In Modern C++** 4 | 5 | [![Build Status](https://travis-ci.org/jonathan-daniel/ModernDesignPatterns.svg?branch=master)](https://travis-ci.org/jonathan-daniel/ModernDesignPatterns) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) 6 | 7 | This repository contains samples of common design patterns written in modern C++. 8 | 9 | This is intended for learning purposes of new C++ features and a starting point for implementing a pattern in your system. 10 | 11 | Every subfolder contains another *README.md* with detailed explanation. 12 | 13 | ## Patterns 14 | 15 | The patterns included in this repo are: 16 | 17 | - [x] [Strategy & Prototype Pattern](StrategyPrototypePattern/) 18 | - [x] [Observer Pattern](ObserverPattern/) 19 | - [x] [Factory Pattern](FactoryPattern/) 20 | - [x] [Abstract Factory Pattern](AbstractFactoryPattern/) 21 | - [x] [Singleton Pattern](SingletonPattern/) 22 | 23 | ## Building 24 | 25 | To build the project (Unix): 26 | 27 | ```bash 28 | $ mkdir build && cd build/ 29 | $ cmake ../ 30 | $ make 31 | ``` 32 | 33 | `cmake` and a GCC version that supports C++17 are required for this. 34 | 35 | ## Contributing 36 | 37 | If there are any better alternatives or bugs I would like to hear it. 38 | -------------------------------------------------------------------------------- /FactoryPattern/README.md: -------------------------------------------------------------------------------- 1 | # Factory Pattern 2 | 3 | This is a factory pattern written in just a few lines of code. 4 | 5 | ```C++ 6 | template 7 | using Factory = std::function; 8 | ``` 9 | Basically a `Factory` is a std::function that has a target callable that's based on parameters/arguments you define. This target callable will return an object of type `ReturnType` defined for the `Factory`. 10 | 11 | An example of how to create a factory for a vector of _N_ Animals: 12 | 13 | ```C++ 14 | /** 15 | * This factory type must return a vector of Animal objects. 16 | */ 17 | using FarmFactory = factory::Factory>; 18 | 19 | FarmFactory farmFactory(int N) 20 | { 21 | return [=] 22 | { 23 | std::vector ret; 24 | for (std::size_t i = 0; i < N; i++) 25 | ret.push_back(Animal()); 26 | 27 | return ret; 28 | }; 29 | } 30 | 31 | // Create a farm with 20 animals 32 | auto mediumFarmFactory = animalfactory::farmFactory(20); // Set the target based on parameters. 33 | auto animals = mediumFarmFactory(); // Call the target of the std::function 34 | 35 | ``` 36 | ## Sequence Diagram 37 | 38 | ![alternative text](http://www.plantuml.com/plantuml/proxy?src=https://raw.githubusercontent.com/jonathan-daniel/ModernDesignPatterns/master/FactoryPattern/doc/factory_diagram.txt&fmt=svg) 39 | -------------------------------------------------------------------------------- /FactoryPattern/AnimalFactory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Factory.hpp" 4 | #include "Animals.hpp" 5 | 6 | #include 7 | #include 8 | 9 | namespace animalfactory 10 | { 11 | 12 | /** 13 | * This is using declaration for an AnimalFactory which is infact a Factory 14 | * that must return a std::unique_ptr. 15 | */ 16 | using AnimalFactory = factory::Factory>; 17 | 18 | 19 | /** 20 | * @brief This functions returns an AnimalFactory which can be used to 21 | * generate a std::unique_ptr as defined for AnimalFactory. 22 | * What derived (or base) object the returned factory makes depends on 23 | * the given _tag_. 24 | * 25 | * @param[in] tag The tag that defines what object the returned 26 | * AnimalFactory makes. 27 | * 28 | * @return returns a callable that generates an object. 29 | */ 30 | AnimalFactory animalFactory(const std::string& tag = {}) 31 | { 32 | return [=] 33 | { 34 | if (tag == "dog") 35 | return std::unique_ptr(new Dog()); 36 | else if (tag == "duck") 37 | return std::unique_ptr(new Duck()); 38 | else if (tag == "special_dog") 39 | return std::unique_ptr(new Dog(true)); 40 | else 41 | return std::unique_ptr(new Animal()); 42 | }; 43 | } 44 | 45 | /** 46 | * This factory type must return a vector of Animal objects. 47 | */ 48 | using FarmFactory = factory::Factory>; 49 | 50 | FarmFactory farmFactory(std::size_t animalcount) 51 | { 52 | return [=] 53 | { 54 | std::vector ret; 55 | for (std::size_t i = 0; i < animalcount; i++) 56 | ret.push_back(Animal()); 57 | 58 | return ret; 59 | }; 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /ObserverPattern/Subject.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | template 9 | class Subject { 10 | private: 11 | std::unordered_multimap< 12 | std::string, 13 | std::function 14 | > observersMap = {}; 15 | 16 | StateType state; 17 | 18 | protected: 19 | void notifyObservers() const 20 | { 21 | // C++17 only 22 | for (const auto & [key, observer] : observersMap) 23 | observer(state); 24 | } 25 | 26 | public: 27 | Subject() : state() {} 28 | 29 | /** 30 | * @brief Sets the state of the subject and 31 | * notifies all attached observers. 32 | * 33 | * @param[in] state The new state. 34 | */ 35 | void setState(const StateType& state) 36 | { 37 | this->state = state; 38 | notifyObservers(); 39 | } 40 | 41 | /** 42 | * @brief Get the current state. 43 | * 44 | * @return The state. 45 | */ 46 | StateType getState() { return state; } 47 | 48 | /** 49 | * @brief Attaches an observer to the subject. 50 | * 51 | * @param[in] observer The observer 52 | * must be of type std::function 61 | void attach(Observer observer, const std::string& key = "") 62 | { 63 | observersMap.insert(std::make_pair(key, observer)); 64 | } 65 | 66 | /** 67 | * @brief Detaches all observers associated with key. 68 | * 69 | * @param[in] key Optional, the key used to attach the observer. 70 | * If multiple observers are attached to this key they 71 | * will all be detached. If no key is specified all observers 72 | * that were attached without key will get detached. 73 | */ 74 | void detach(const std::string& key = "") 75 | { 76 | observersMap.erase(key); 77 | } 78 | 79 | /** 80 | * @brief Detaches all attached observers. 81 | */ 82 | void detachAll() 83 | { 84 | observersMap.clear(); 85 | } 86 | }; --------------------------------------------------------------------------------