#include
using namespace std;
/**
@file
@brief skeleton C++ examples of OOP and Design patterns
2021 Constantine Shulyupin
Patterns help to conform to [SOLID principles](https://en.wikipedia.org/wiki/SOLID):
- [Single-responsibility](https://en.wikipedia.org/wiki/Single-responsibility_principle)
- high [cohesion](https://en.wikipedia.org/wiki/Cohesion_(computer_science))
- [Open-closed](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle)
- open for extension
- but closed for modification
- [Liskov substitution](https://en.wikipedia.org/wiki/Liskov_substitution_principle)
- [composition over inheritance](https://en.wikipedia.org/wiki/Composition_over_inheritance)
- [Interface segregation](https://en.wikipedia.org/wiki/Interface_segregation_principle)
- use many specific interfaces
- [Dependency inversion](https://en.wikipedia.org/wiki/Dependency_inversion_principle)
- don't depend on implementations
- depend on interfaces
- [Loose coupling](https://en.wikipedia.org/wiki/Loose_coupling)
Coding style:
Using struct because is it like class with default public members and methods.
Less is more. Skeleton code with minimal optional code and duplications.
Each word "Sample" in an inventer assumes multiple instances like Sample1, Sample2 ... SampleN
Contents
- @ref OOP
- @ref DP
*/
/**
@defgroup OOP C++ OOP patterns
@brief some examples in C++
Four Pillars of
[Object-oriented_programming](https://en.wikipedia.org/wiki/Object-oriented_programming):
- [Abstraction](https://en.wikipedia.org/wiki/Abstraction_(computer_science))
- [Encapsulation](https://en.wikipedia.org/wiki/Encapsulation_(computer_programming))
- [Inheritance](https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming))
- [Polymorphism](https://en.wikipedia.org/wiki/Polymorphism_(computer_science))
[object-oriented-programming-in-cpp](https://www.geeksforgeeks.org/object-oriented-programming-in-cpp)
[class](https://en.cppreference.com/w/cpp/language/class)
[derived_class](https://en.cppreference.com/w/cpp/language/derived_class)
[abstract_class](https://en.cppreference.com/w/cpp/language/abstract_class)
[shared_lock](https://en.cppreference.com/w/cpp/thread/shared_lock)
Three independent interfaces
Setter_interface, Getter_interface and Change_interface demonstrate
[Interface segregation](https://en.wikipedia.org/wiki/Interface_segregation_principle)
[Use multiple inheritance to represent multiple distinct interfaces](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c135-use-multiple-inheritance-to-represent-multiple-distinct-interfaces)
@{
*/
struct Module { };
/// [Object composition](https://en.wikipedia.org/wiki/Object_composition)
struct Composition {
Module m1, m2;
};
/** [Aggregation](https://en.wikipedia.org/wiki/Object_composition#Aggregation)
by reference
*/
struct Reference {
Module &m1, &m2;
Reference() = default;
};
/** [Pointer](https://en.wikipedia.org/wiki/Pointer_(computer_programming)#C_and_C++)
is also can be used for aggregation
*/
struct Pointer {
Module* ptr;
shared_ptr sm;
unique_ptr um;
};
/// [Associations](https://en.wikipedia.org/wiki/Association_(object-oriented_programming))
void associations_demo()
{
Module m1, m2;
Reference r { m1, m2 };
Pointer p { &m1 };
// References can't be changed or zeroed like pointers during runtime.
p.ptr = nullptr;
}
/// @brief is a sample of setter abstract interface for Synchronized_encapsulated_value
template
struct Setter_interface {
virtual void set(ValueType i) = 0;
virtual ~Setter_interface() noexcept = default;
};
template
struct Getter_interface
/// @brief is a sample of getter abstract interface for Synchronized_encapsulated_value
{
virtual ValueType get() const = 0;
virtual ~Getter_interface() noexcept = default;
};
template
struct Change_interface
/// @brief is a sample of changer abstract interface for Synchronized_encapsulated_value
{
virtual void change(ValueType c) = 0;
virtual ~Change_interface() noexcept = default;
};
template
class Synchronized_encapsulated_value
/**
@brief [encapsulating](https://en.wikipedia.org/wiki/Encapsulation_(computer_programming))
class with only public accessor and [mutator](https://en.wikipedia.org/wiki/Mutator_method) intrfaces
Classes by default are private. This class doesn't contain public members.
Uses [Readerswriter_lock](https://en.wikipedia.org/wiki/Readerswriter_lock).
See also:
- [Boost synchronized values](https://www.boost.org/doc/libs/release/doc/html/thread/sds.html#thread.sds.synchronized_valuesxxx)
- [boost/thread/synchronized_value.hpp](https://github.com/boostorg/thread/blob/HEAD/include/boost/thread/synchronized_value.hpp)
*/
: public Setter_interface,
public Getter_interface,
public Change_interface {
void set(ValueType i) override
{
scoped_lock writer_lock(mtx);
value = i;
}
ValueType get() const override
{
shared_lock reader_lock(mtx); /// [reader writer locks](https://www.modernescpp.com/index.php/reader-writer-locks)
return value;
}
void change(ValueType c) override
{
scoped_lock writer_lock(mtx);
value += c;
}
mutable shared_mutex mtx; ///< [shared_mutex](https://en.cppreference.com/w/cpp/thread/shared_mutex)
ValueType value;
};
void synchronized_encapsulated_value()
/**
Lambda expression 'client' demonstrates [Dependency inversion](https://en.wikipedia.org/wiki/Dependency_inversion_principle) -
it doesn't depends from implementation Synchronized_encapsulated_value but depends only from interfaces.
*/
{
auto client = [](Setter_interface& s, Getter_interface& g) {
s.set(1);
assert(g.get() == 1);
};
Synchronized_encapsulated_value v;
Setter_interface& s = v;
Getter_interface& g = v;
client(s, g);
auto client2 = [](Setter_interface& s, Getter_interface& g) {
s.set("abc");
assert(g.get() == "abc");
};
Synchronized_encapsulated_value v2;
Setter_interface& s2(v2);
Getter_interface& g2(v2);
Change_interface& c2(v2);
client2(s2, g2);
c2.change("de");
assert(g2.get() == "abcde");
}
void oop_demo()
{
associations_demo();
synchronized_encapsulated_value();
}
/// @}
/**
@defgroup DP Design patterns skeleton examples
@brief [Software design patterns](https://en.wikipedia.org/wiki/Software_design_pattern)
https://refactoring.guru/design-patterns
[C++ Programming: Code patterns design](https://en.wikibooks.org/wiki/C++_Programming/Code/Design_Patterns)
Disclaimer:
Example code below for simplicity doesn't utilize synchronization,
privatization and other administrative functions.
Don't forget to add locking, synchronization, encapsulation, privatization,
protection manually where it is required when using examples below.
@{
*/
struct Interface
/// @brief is a common pure virtual interface
{
virtual int method() = 0;
virtual ~Interface() noexcept = default;
};
/**
@defgroup CP Creational
@brief [Creational patterns](https://en.wikipedia.org/wiki/Creational_pattern)
https://refactoring.guru/design-patterns/creational-patterns
@{
*/
/**
The singleton will be automatically safely instantiated on the first call.
Define constructor body after the define */
#define SINGLETON(Singleton) \
public: \
/* Meyers Singleton realization */ \
static Singleton& instance() \
{ \
static Singleton me; \
return me; \
} \
Singleton(const Singleton&) = delete; \
Singleton& operator=(const Singleton&) = delete; \
Singleton(Singleton&&) = delete; \
Singleton& operator=(Singleton&&) = delete; \
\
private: \
Singleton()
struct Singleton_demo {
SINGLETON(Singleton_demo) {};
};
struct Factory_method_demo {
virtual unique_ptr factory_method() = 0;
int client()
{
auto p(factory_method());
return p->method();
};
};
struct Sample_product
: Interface {
int data;
int method() override { return data; }
Sample_product(int d = 0)
: data(d)
{
}
};
struct Sample_factory_method_demo
: Factory_method_demo {
unique_ptr factory_method() override
{
return make_unique(123);
}
};
struct Abstract_factory {
virtual unique_ptr create() = 0;
};
struct Sample_factory
: Abstract_factory {
virtual unique_ptr create()
{
return make_unique();
}
};
struct Prototype
/// @brief is the factory of himself
: Abstract_factory,
Interface {
int method() override { return 1; }
unique_ptr create() override
{
auto clone = new Prototype(*this);
return unique_ptr(clone);
}
};
struct Builder {
int data = 0;
Builder& add(int i)
{
data += i;
return *this;
}
Builder& operatorsubject.method();
}
Interface& subject; // decorated object is public
};
struct Composite
: public Interface {
int method() override
{
for (Interface& i : children)
i.method();
return 0;
}
forward_list children;
};
void structural_patterns_demo()
{
Standalone sa;
Bridge br(sa);
br.method();
Proxy p(br);
Decorator dec(br);
dec.method();
dec.subject.method();
p.method();
Composite comp;
comp.children.push_front(p);
comp.method();
}
/// @} SP
/**
@defgroup BP Behavioral
@brief [Behavioral patterns](https://en.wikipedia.org/wiki/Behavioral_pattern)
https://refactoring.guru/design-patterns/behavioral-patterns
@{
*/
/**
Credit: [observer](https://cpppatterns.com/patterns/observer.html)
*/
struct Subject;
struct Observer
/// @brief is virtual observer of a Subject
{
/// @brief without arguments
virtual void notify() {};
/// @brief with the only Subject argument
virtual void update(Subject& subject) {};
virtual ~Observer() noexcept = default;
};
struct Subject
/// @brief of Observer
{
void notify_observers()
{
for (Observer& o : observers) {
o.notify();
o.update(*this);
}
}
forward_list observers;
};
void observer_demo()
{
Observer o;
Subject s;
s.observers.push_front(o);
s.notify_observers();
}
/**
@defgroup PS Publishsubscribe pattern
@brief [Publishsubscribe pattern](https://en.wikipedia.org/wiki/Publishsubscribe_pattern)
@{
*/
struct Message { };
struct Subscriber {
void message(Message& m) {};
};
struct Publisher {
map topic_subscribers;
void publish(const string& topic, Message& m)
{
for (Subscriber& s : topic_subscribers[topic])
s.message(m);
}
};
void publisher_subscriber_demo()
{
Subscriber sub;
Publisher pub;
pub.topic_subscribers["sample_topic"].push_front(sub);
Message m;
pub.publish("sample_topic", m);
}
/// @} PS
/**
[Mediator_pattern](https://en.wikipedia.org/wiki/Mediator_pattern)
https://refactoring.guru/design-patterns/mediator
*/
struct Mediator;
struct Member {
Mediator* mediator;
void send(Message&);
void receive(Message&) { }
};
struct Mediator {
void register_member(Member& m)
{
m.mediator = this;
members.push_front(m);
}
void dispatch(Message& msg)
{
for (Member& m : members)
m.receive(msg);
}
forward_list members;
};
void Member::send(Message& m)
{
mediator->dispatch(m);
}
void mediator_demo()
{
Member m1, m2;
Mediator md;
md.register_member(m1);
md.register_member(m2);
Message msg;
m1.send(msg);
}
struct Command
/** @brief encapsulates arguments. AKA Intent, operation.
[Command pattern](https://en.wikipedia.org/wiki/Command_pattern)
https://refactoring.guru/design-patterns/command/cpp/example
*/
{
virtual int execute() { return -1; };
};
/**
@defgroup visitor Visitor
@brief [Visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern)
https://refactoring.guru/design-patterns/visitor/cpp/example
@{
*/
struct Abstract_visitor;
struct Component
/// @brief accepts a pure virtual Abstract_visitor
{
virtual string component_accept(Abstract_visitor&) const = 0;
virtual ~Component() = default;
};
string client_visit(const forward_list& components,
const forward_list& visitors)
/// @brief knows only virtual visitor and component
{
string res;
for (auto&& v : visitors)
for (auto&& c : components) {
assert(typeid(c) != typeid(Component));
res += string(__func__) + " > " + c->component_accept(*v.get());
}
return res;
}
struct Sample_component;
struct Abstract_visitor
/// @brief is a pure virtual visitor of Sample_component and other specific components
{
/// overloaded function for each component subtype
virtual string visit(const Sample_component&) const = 0;
virtual ~Abstract_visitor() noexcept = default;
};
struct Sample_component
: public Component
/** @brief one of many components
is independent from Sample_visitor and implementations of function visit.
*/
{
string component_accept(Abstract_visitor& visitor) const override
{
assert(typeid(*this) == typeid(Sample_component));
assert(typeid(visitor) != typeid(Abstract_visitor));
return string(__func__) + " > " + visitor.visit(*this);
}
/// @brief is not virtual
string sample_component_method() const
{
return __func__;
}
};
/**
Call hierarchy:
visitor_demo
client_visit
component_accept
visit
sample_component_method
*/
void visitor_demo()
{
/// @brief is one of many specific visitors with custom method visit
/// Per each of the possible pairs of Sample_visitor and Sample_component
struct Sample_visitor
: public Abstract_visitor {
/// overloaded function for each component
string visit(const Sample_component& sc) const override
{
assert(typeid(*this) == typeid(Sample_visitor));
assert(typeid(sc) == typeid(Sample_component));
return string(__func__) + " > " + sc.sample_component_method();
}
};
forward_list components;
components.emplace_front(new Sample_component);
forward_list visitors;
visitors.emplace_front(new Sample_visitor);
assert(client_visit(components, visitors) == "client_visit > component_accept > visit > sample_component_method");
// flat code of expanded client_visit:
for (auto&& v : visitors) {
if (auto sv = dynamic_cast(v.get()))
for (auto&& c : components) {
if (auto sc = dynamic_cast(c.get()))
// inside component_accept:
sv->visit(*sc);
else {
};
}
else {
/* And so on for each pair of visitor and component.
Total number of pairs is multiplication of
number components and number of visitors.
*/
};
}
}
// std::visit demo:
static stringstream output;
struct Visitor_op {
void operator()(int arg) { output