Reset();
91 | }
92 |
93 | ~ConcreteBuilder1(){
94 | delete product;
95 | }
96 |
97 | void Reset(){
98 | this->product= new Product1();
99 | }
100 | /**
101 | * EN: All production steps work with the same product instance.
102 | *
103 | * RU: Все этапы производства работают с одним и тем же экземпляром
104 | * продукта.
105 | */
106 |
107 | void ProducePartA()const override{
108 | this->product->parts_.push_back("PartA1");
109 | }
110 |
111 | void ProducePartB()const override{
112 | this->product->parts_.push_back("PartB1");
113 | }
114 |
115 | void ProducePartC()const override{
116 | this->product->parts_.push_back("PartC1");
117 | }
118 |
119 | /**
120 | * EN: Concrete Builders are supposed to provide their own methods for
121 | * retrieving results. That's because various types of builders may create
122 | * entirely different products that don't follow the same interface.
123 | * Therefore, such methods cannot be declared in the base Builder interface
124 | * (at least in a statically typed programming language). Note that PHP is a
125 | * dynamically typed language and this method CAN be in the base interface.
126 | * However, we won't declare it there for the sake of clarity.
127 | *
128 | * Usually, after returning the end result to the client, a builder instance
129 | * is expected to be ready to start producing another product. That's why
130 | * it's a usual practice to call the reset method at the end of the
131 | * `getProduct` method body. However, this behavior is not mandatory, and
132 | * you can make your builders wait for an explicit reset call from the
133 | * client code before disposing of the previous result.
134 | *
135 | * RU: Конкретные Строители должны предоставить свои собственные методы
136 | * получения результатов. Это связано с тем, что различные типы строителей
137 | * могут создавать совершенно разные продукты с разными интерфейсами.
138 | * Поэтому такие методы не могут быть объявлены в базовом интерфейсе
139 | * Строителя (по крайней мере, в статически типизированном языке
140 | * программирования). Обратите внимание, что PHP является динамически
141 | * типизированным языком, и этот метод может быть в базовом интерфейсе.
142 | * Однако мы не будем объявлять его здесь для ясности.
143 | *
144 | * Как правило, после возвращения конечного результата клиенту, экземпляр
145 | * строителя должен быть готов к началу производства следующего продукта.
146 | * Поэтому обычной практикой является вызов метода сброса в конце тела
147 | * метода getProduct. Однако такое поведение не является обязательным, вы
148 | * можете заставить своих строителей ждать явного запроса на сброс из кода
149 | * клиента, прежде чем избавиться от предыдущего результата.
150 | */
151 |
152 | /**
153 | * EN: Please be careful here with the memory ownership. Once you call GetProduct
154 | * the user of this function is responsable to release this memory. Here could be
155 | * a better option to use smart pointers to avoid memory leaks
156 | *
157 | * RU:
158 | *
159 | */
160 |
161 | Product1* GetProduct() {
162 | Product1* result= this->product;
163 | this->Reset();
164 | return result;
165 | }
166 | };
167 |
168 | /**
169 | * EN: The Director is only responsible for executing the building steps in a
170 | * particular sequence. It is helpful when producing products according to a
171 | * specific order or configuration. Strictly speaking, the Director class is
172 | * optional, since the client can control builders directly.
173 | *
174 | * RU: Директор отвечает только за выполнение шагов построения в определённой
175 | * последовательности. Это полезно при производстве продуктов в определённом
176 | * порядке или особой конфигурации. Строго говоря, класс Директор необязателен,
177 | * так как клиент может напрямую управлять строителями.
178 | */
179 | class Director{
180 | /**
181 | * @var Builder
182 | */
183 | private:
184 | Builder* builder;
185 | /**
186 | * EN: The Director works with any builder instance that the client code
187 | * passes to it. This way, the client code may alter the final type of the
188 | * newly assembled product.
189 | *
190 | * RU: Директор работает с любым экземпляром строителя, который передаётся
191 | * ему клиентским кодом. Таким образом, клиентский код может изменить
192 | * конечный тип вновь собираемого продукта.
193 | */
194 |
195 | public:
196 |
197 | void set_builder(Builder* builder){
198 | this->builder=builder;
199 | }
200 |
201 | /**
202 | * EN: The Director can construct several product variations using the same
203 | * building steps.
204 | *
205 | * RU: Директор может строить несколько вариаций продукта, используя
206 | * одинаковые шаги построения.
207 | */
208 |
209 | void BuildMinimalViableProduct(){
210 | this->builder->ProducePartA();
211 | }
212 |
213 | void BuildFullFeaturedProduct(){
214 | this->builder->ProducePartA();
215 | this->builder->ProducePartB();
216 | this->builder->ProducePartC();
217 | }
218 | };
219 | /**
220 | * EN: The client code creates a builder object, passes it to the director and
221 | * then initiates the construction process. The end result is retrieved from the
222 | * builder object.
223 | *
224 | * RU: Клиентский код создаёт объект-строитель, передаёт его директору, а затем
225 | * инициирует процесс построения. Конечный результат извлекается из
226 | * объекта-строителя.
227 | */
228 | /**
229 | * EN: I used raw pointers for simplicity however you may prefer to use smart pointers here
230 | *
231 | * RU:
232 | *
233 | */
234 | void ClientCode(Director& director)
235 | {
236 | ConcreteBuilder1* builder = new ConcreteBuilder1();
237 | director.set_builder(builder);
238 | std::cout << "Standard basic product:\n";
239 | director.BuildMinimalViableProduct();
240 |
241 | Product1* p= builder->GetProduct();
242 | p->ListParts();
243 | delete p;
244 |
245 | std::cout << "Standard full featured product:\n";
246 | director.BuildFullFeaturedProduct();
247 |
248 | p= builder->GetProduct();
249 | p->ListParts();
250 | delete p;
251 |
252 | // EN: Remember, the Builder pattern can be used without a Director class.
253 | //
254 | // RU: Помните, что паттерн Строитель можно использовать без класса
255 | // Директор.
256 | std::cout << "Custom product:\n";
257 | builder->ProducePartA();
258 | builder->ProducePartC();
259 | p=builder->GetProduct();
260 | p->ListParts();
261 | delete p;
262 |
263 | delete builder;
264 | }
265 |
266 | int main(){
267 | Director* director= new Director();
268 | ClientCode(*director);
269 | delete director;
270 | return 0;
271 | }
272 |
--------------------------------------------------------------------------------
/src/Builder/RealWorld/Output.txt:
--------------------------------------------------------------------------------
1 |
2 | Title of the Page
3 | Subtitle A
4 | Lorem ipsum dolor sit amet, ...
5 | Subtitle B
6 | ... consectetur adipiscing elit.
7 |
--------------------------------------------------------------------------------
/src/Builder/RealWorld/main.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | /**
6 | * EN: Real World Example for the Builder Design Pattern (C++03/11 Evolution)
7 | *
8 | * Need: Consider a representation of the Document Object Model in which
9 | * each HTML element is a non-trivial graph (multi-way tree) structure
10 | * whose construction is complicated by the need to add an arbitrary number of
11 | * children to the root.
12 | *
13 | * Solution: A HTML ElementBuilder can be used for stepwise construction of an
14 | * Element using an implementational variant of the Builder Design Pattern
15 | * known as the \e Fluent Builder. Although modern C++17/20/23 provides the
16 | * neccesary built-in language mechanics (i.e. initializer_list and parameter
17 | * packs) for a Builder, this specific \e Fluent Builder is a class that can be
18 | * applied to legacy code relying on the older C++03/11 standards.
19 | */
20 |
21 | #include
22 | #include
23 | #include
24 |
25 | /**
26 | * EN: These preprocessor directives allow this standalone code to target both
27 | * pre- and post-C++11 standards when it comes to std::vector, in particular,
28 | * appending a new element as well as iterating over all of the elements. In
29 | * addition, unscoped C++03 and scoped C++11 enums are also handled using the
30 | * same technique. However, this approach is for only demonstration purposes in
31 | * order to show the subtle difference in the C++03- and C++11-subvariants of
32 | * the Fluent Builder as part of the evolution of the design pattern itself.
33 | */
34 | #if (defined(_MSVC_LANG) && _MSVC_LANG >= 201103L) || \
35 | ((!defined(_MSVC_LANG)) && __cplusplus >= 201103L)
36 |
37 | #define append_element(tag, content) emplace_back(tag, content)
38 | #define ranged_for(children) for (auto const &child : children)
39 |
40 | #define ENUMMERATION_TYPE() enum class
41 | #define TAG_SCOPE() html::Tag
42 |
43 | #else
44 |
45 | #define append_element(tag, content) push_back(Element(tag, content))
46 | #define ranged_for(children) \
47 | for (std::vector::const_iterator it = children.begin(); \
48 | it != children.end(); ++it)
49 | #define child *it
50 |
51 | #define ENUMMERATION_TYPE() enum
52 | #define TAG_SCOPE() html
53 |
54 | #endif
55 |
56 | /**
57 | * EN: The html namespace contains the core machinery of the Fluent Builder
58 | * Pattern, namely, the Element and ElementBuilder classes. To showcase the
59 | * versatility of the pattern in being able to extend the Element class with
60 | * different types of HTML elements (tags), a print method that relies on
61 | * various tags is provided to show the Fluent Builder in action.
62 | */
63 | namespace html {
64 |
65 | /**
66 | * EN: The forward declaration for the ElementBuilder is necessary as it is
67 | * a friend class of the Element class in this Fluent Builder implementation.
68 | */
69 | class ElementBuilder;
70 |
71 | /**
72 | * EN: Enumeration to represent different HTML elements. (Note that in C++11
73 | * the enumeration will be class-scoped.) There is also a naive helper function
74 | * to convert the names into strings, which is used inside of the print method.
75 | */
76 | ENUMMERATION_TYPE() Tag{body, h1, h2, p, /* ... */};
77 |
78 | std::string to_string(Tag tag) {
79 | switch (tag) {
80 | case TAG_SCOPE()::body:
81 | return "body";
82 | case TAG_SCOPE()::h1:
83 | return "h1";
84 | case TAG_SCOPE()::h2:
85 | return "h2";
86 | case TAG_SCOPE()::p:
87 | return "p";
88 | /* ... */
89 | default:
90 | return "tag";
91 | }
92 | }
93 |
94 | /**
95 | * EN: This client-facing Element class is essentially a tree node that
96 | * stores its children by value in a dynamic container. The Fluent Builder
97 | * provides a means to construct an instance of root Element node and then add
98 | * an arbitrary number of children Element nodes.
99 | */
100 | class Element {
101 | public:
102 | Element(Tag tag, std::string const &content = std::string())
103 | : tag_(tag), content_(content) {}
104 |
105 | /**
106 | * EN: The print method generates markup. Note that the ranged-based for
107 | * loop over the children differs between the respective C++03 and C++11
108 | * standards.
109 | */
110 | friend std::ostream &operator<<(std::ostream &os, Element const &e) {
111 | os << "<" << to_string(e.tag_) << ">";
112 | if (!e.content_.empty()) {
113 | os << e.content_;
114 | } else {
115 | os << "\n";
116 | }
117 | ranged_for(e.children_) { os << child; }
118 | os << "" << to_string(e.tag_) << ">\n";
119 | return os;
120 | }
121 |
122 | private:
123 | friend class ElementBuilder;
124 |
125 | private:
126 | Tag tag_;
127 | std::string content_;
128 | std::vector children_;
129 | };
130 |
131 | /**
132 | * EN: The Fluent Builder is named for its method chaining as the modifier
133 | * (setter) method add_child() returns the builder itself, and so it can be
134 | * repeatedly called to construct a complex Element with many Element children.
135 | *
136 | * Again note that that element addition operation on the vector of children
137 | * differs between the C++03 and C++11 standards; in the former case, the
138 | * Element constructor must be called explicitly whereas in the latter case, the
139 | * arguments are forwarded to the Element constructor.
140 | */
141 | class ElementBuilder {
142 | public:
143 | explicit ElementBuilder(Tag tag, std::string const &content = std::string())
144 | : root_(Element(tag, content)) {}
145 |
146 | ElementBuilder &add_child(Tag tag,
147 | std::string const &content = std::string()) {
148 | root_.children_.append_element(tag, content);
149 | return *this;
150 | }
151 |
152 | operator Element() const { return root_; }
153 |
154 | private:
155 | Element root_;
156 | };
157 |
158 | } // namespace html
159 |
160 | int main() {
161 | html::Element body =
162 | html::ElementBuilder(TAG_SCOPE()::body)
163 | .add_child(TAG_SCOPE()::h1, "Title of the Page")
164 | .add_child(TAG_SCOPE()::h2, "Subtitle A")
165 | .add_child(TAG_SCOPE()::p, "Lorem ipsum dolor sit amet, ...")
166 | .add_child(TAG_SCOPE()::h2, "Subtitle B")
167 | .add_child(TAG_SCOPE()::p, "... consectetur adipiscing elit.")
168 | /* ... */;
169 |
170 | std::cout << body;
171 | }
--------------------------------------------------------------------------------
/src/ChainOfResponsibility/Conceptual/Output.txt:
--------------------------------------------------------------------------------
1 | Chain: Monkey > Squirrel > Dog
2 |
3 | Client: Who wants a Nut?
4 | Squirrel: I'll eat the Nut.
5 | Client: Who wants a Banana?
6 | Monkey: I'll eat the Banana.
7 | Client: Who wants a Cup of coffee?
8 | Cup of coffee was left untouched.
9 |
10 | Subchain: Squirrel > Dog
11 |
12 | Client: Who wants a Nut?
13 | Squirrel: I'll eat the Nut.
14 | Client: Who wants a Banana?
15 | Banana was left untouched.
16 | Client: Who wants a Cup of coffee?
17 | Cup of coffee was left untouched.
--------------------------------------------------------------------------------
/src/ChainOfResponsibility/Conceptual/main.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | /**
6 | * EN: Chain of Responsibility Design Pattern
7 | *
8 | * Intent: Lets you pass requests along a chain of handlers. Upon receiving a
9 | * request, each handler decides either to process the request or to pass it to
10 | * the next handler in the chain.
11 | *
12 | * RU: Паттерн Цепочка обязанностей
13 | *
14 | * Назначение: Позволяет передавать запросы последовательно по цепочке
15 | * обработчиков. Каждый последующий обработчик решает, может ли он обработать
16 | * запрос сам и стоит ли передавать запрос дальше по цепи.
17 | */
18 | /**
19 | * EN: The Handler interface declares a method for building the chain of
20 | * handlers. It also declares a method for executing a request.
21 | *
22 | * RU: Интерфейс Обработчика объявляет метод построения цепочки обработчиков. Он
23 | * также объявляет метод для выполнения запроса.
24 | */
25 | class Handler {
26 | public:
27 | virtual Handler *SetNext(Handler *handler) = 0;
28 | virtual std::string Handle(std::string request) = 0;
29 | };
30 | /**
31 | * EN: The default chaining behavior can be implemented inside a base handler
32 | * class.
33 | *
34 | * RU: Поведение цепочки по умолчанию может быть реализовано внутри базового
35 | * класса обработчика.
36 | */
37 | class AbstractHandler : public Handler {
38 | /**
39 | * @var Handler
40 | */
41 | private:
42 | Handler *next_handler_;
43 |
44 | public:
45 | AbstractHandler() : next_handler_(nullptr) {
46 | }
47 | Handler *SetNext(Handler *handler) override {
48 | this->next_handler_ = handler;
49 | // EN: Returning a handler from here will let us link handlers in a
50 | // convenient way like this:
51 | // $monkey->setNext($squirrel)->setNext($dog);
52 | //
53 | // RU: Возврат обработчика отсюда позволит связать обработчики простым
54 | // способом, вот так:
55 | // $monkey->setNext($squirrel)->setNext($dog);
56 | return handler;
57 | }
58 | std::string Handle(std::string request) override {
59 | if (this->next_handler_) {
60 | return this->next_handler_->Handle(request);
61 | }
62 |
63 | return {};
64 | }
65 | };
66 | /**
67 | * EN: All Concrete Handlers either handle a request or pass it to the next
68 | * handler in the chain.
69 | *
70 | * RU: Все Конкретные Обработчики либо обрабатывают запрос, либо передают его
71 | * следующему обработчику в цепочке.
72 | */
73 | class MonkeyHandler : public AbstractHandler {
74 | public:
75 | std::string Handle(std::string request) override {
76 | if (request == "Banana") {
77 | return "Monkey: I'll eat the " + request + ".\n";
78 | } else {
79 | return AbstractHandler::Handle(request);
80 | }
81 | }
82 | };
83 | class SquirrelHandler : public AbstractHandler {
84 | public:
85 | std::string Handle(std::string request) override {
86 | if (request == "Nut") {
87 | return "Squirrel: I'll eat the " + request + ".\n";
88 | } else {
89 | return AbstractHandler::Handle(request);
90 | }
91 | }
92 | };
93 | class DogHandler : public AbstractHandler {
94 | public:
95 | std::string Handle(std::string request) override {
96 | if (request == "MeatBall") {
97 | return "Dog: I'll eat the " + request + ".\n";
98 | } else {
99 | return AbstractHandler::Handle(request);
100 | }
101 | }
102 | };
103 | /**
104 | * EN: The client code is usually suited to work with a single handler. In most
105 | * cases, it is not even aware that the handler is part of a chain.
106 | *
107 | * RU: Обычно клиентский код приспособлен для работы с единственным
108 | * обработчиком. В большинстве случаев клиенту даже неизвестно, что этот
109 | * обработчик является частью цепочки.
110 | */
111 | void ClientCode(Handler &handler) {
112 | std::vector food = {"Nut", "Banana", "Cup of coffee"};
113 | for (const std::string &f : food) {
114 | std::cout << "Client: Who wants a " << f << "?\n";
115 | const std::string result = handler.Handle(f);
116 | if (!result.empty()) {
117 | std::cout << " " << result;
118 | } else {
119 | std::cout << " " << f << " was left untouched.\n";
120 | }
121 | }
122 | }
123 | /**
124 | * EN: The other part of the client code constructs the actual chain.
125 | *
126 | * RU: Другая часть клиентского кода создает саму цепочку.
127 | */
128 | int main() {
129 | MonkeyHandler *monkey = new MonkeyHandler;
130 | SquirrelHandler *squirrel = new SquirrelHandler;
131 | DogHandler *dog = new DogHandler;
132 | monkey->SetNext(squirrel)->SetNext(dog);
133 |
134 | /**
135 | * EN: The client should be able to send a request to any handler, not just the
136 | * first one in the chain.
137 | *
138 | * RU: Клиент должен иметь возможность отправлять запрос любому обработчику, а
139 | * не только первому в цепочке.
140 | */
141 | std::cout << "Chain: Monkey > Squirrel > Dog\n\n";
142 | ClientCode(*monkey);
143 | std::cout << "\n";
144 | std::cout << "Subchain: Squirrel > Dog\n\n";
145 | ClientCode(*squirrel);
146 |
147 | delete monkey;
148 | delete squirrel;
149 | delete dog;
150 |
151 | return 0;
152 | }
153 |
--------------------------------------------------------------------------------
/src/Command/Conceptual/Output.txt:
--------------------------------------------------------------------------------
1 | Invoker: Does anybody want something done before I begin?
2 | SimpleCommand: See, I can do simple things like printing (Say Hi!)
3 | Invoker: ...doing something really important...
4 | Invoker: Does anybody want something done after I finish?
5 | ComplexCommand: Complex stuff should be done by a receiver object.
6 | Receiver: Working on (Send email.)
7 | Receiver: Also working on (Save report.)
--------------------------------------------------------------------------------
/src/Command/Conceptual/main.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | /**
5 | * EN: Command Design Pattern
6 | *
7 | * Intent: Turns a request into a stand-alone object that contains all
8 | * information about the request. This transformation lets you parameterize
9 | * methods with different requests, delay or queue a request's execution, and
10 | * support undoable operations.
11 | *
12 | * RU: Паттерн Команда
13 | *
14 | * Назначение: Превращает запросы в объекты, позволяя передавать их как
15 | * аргументы при вызове методов, ставить запросы в очередь, логировать их, а
16 | * также поддерживать отмену операций.
17 | */
18 | /**
19 | * EN: The Command interface declares a method for executing a command.
20 | *
21 | * RU: Интерфейс Команды объявляет метод для выполнения команд.
22 | */
23 | class Command {
24 | public:
25 | virtual ~Command() {
26 | }
27 | virtual void Execute() const = 0;
28 | };
29 | /**
30 | * EN: Some commands can implement simple operations on their own.
31 | *
32 | * RU: Некоторые команды способны выполнять простые операции самостоятельно.
33 | */
34 | class SimpleCommand : public Command {
35 | private:
36 | std::string pay_load_;
37 |
38 | public:
39 | explicit SimpleCommand(std::string pay_load) : pay_load_(pay_load) {
40 | }
41 | void Execute() const override {
42 | std::cout << "SimpleCommand: See, I can do simple things like printing (" << this->pay_load_ << ")\n";
43 | }
44 | };
45 |
46 | /**
47 | * EN: The Receiver classes contain some important business logic. They know how
48 | * to perform all kinds of operations, associated with carrying out a request.
49 | * In fact, any class may serve as a Receiver.
50 | *
51 | * RU: Классы Получателей содержат некую важную бизнес-логику. Они умеют
52 | * выполнять все виды операций, связанных с выполнением запроса. Фактически,
53 | * любой класс может выступать Получателем.
54 | */
55 | class Receiver {
56 | public:
57 | void DoSomething(const std::string &a) {
58 | std::cout << "Receiver: Working on (" << a << ".)\n";
59 | }
60 | void DoSomethingElse(const std::string &b) {
61 | std::cout << "Receiver: Also working on (" << b << ".)\n";
62 | }
63 | };
64 |
65 | /**
66 | * EN: However, some commands can delegate more complex operations to other
67 | * objects, called "receivers."
68 | *
69 | * RU: Но есть и команды, которые делегируют более сложные операции другим
70 | * объектам, называемым «получателями».
71 | */
72 | class ComplexCommand : public Command {
73 | /**
74 | * @var Receiver
75 | */
76 | private:
77 | Receiver *receiver_;
78 | /**
79 | * EN: Context data, required for launching the receiver's methods.
80 | *
81 | * RU: Данные о контексте, необходимые для запуска методов получателя.
82 | */
83 | std::string a_;
84 | std::string b_;
85 | /**
86 | * EN: Complex commands can accept one or several receiver objects along
87 | * with any context data via the constructor.
88 | *
89 | * RU: Сложные команды могут принимать один или несколько
90 | * объектов-получателей вместе с любыми данными о контексте через
91 | * конструктор.
92 | */
93 | public:
94 | ComplexCommand(Receiver *receiver, std::string a, std::string b) : receiver_(receiver), a_(a), b_(b) {
95 | }
96 | /**
97 | * EN: Commands can delegate to any methods of a receiver.
98 | *
99 | * RU: Команды могут делегировать выполнение любым методам получателя.
100 | */
101 | void Execute() const override {
102 | std::cout << "ComplexCommand: Complex stuff should be done by a receiver object.\n";
103 | this->receiver_->DoSomething(this->a_);
104 | this->receiver_->DoSomethingElse(this->b_);
105 | }
106 | };
107 |
108 | /**
109 | * EN: The Invoker is associated with one or several commands. It sends a
110 | * request to the command.
111 | *
112 | * RU: Отправитель связан с одной или несколькими командами. Он отправляет
113 | * запрос команде.
114 | */
115 | class Invoker {
116 | /**
117 | * @var Command
118 | */
119 | private:
120 | Command *on_start_;
121 | /**
122 | * @var Command
123 | */
124 | Command *on_finish_;
125 | /**
126 | * EN: Initialize commands.
127 | *
128 | * RU: Инициализация команд.
129 | */
130 | public:
131 | ~Invoker() {
132 | delete on_start_;
133 | delete on_finish_;
134 | }
135 |
136 | void SetOnStart(Command *command) {
137 | this->on_start_ = command;
138 | }
139 | void SetOnFinish(Command *command) {
140 | this->on_finish_ = command;
141 | }
142 | /**
143 | * EN: The Invoker does not depend on concrete command or receiver classes.
144 | * The Invoker passes a request to a receiver indirectly, by executing a
145 | * command.
146 | *
147 | * RU: Отправитель не зависит от классов конкретных команд и получателей.
148 | * Отправитель передаёт запрос получателю косвенно, выполняя команду.
149 | */
150 | void DoSomethingImportant() {
151 | std::cout << "Invoker: Does anybody want something done before I begin?\n";
152 | if (this->on_start_) {
153 | this->on_start_->Execute();
154 | }
155 | std::cout << "Invoker: ...doing something really important...\n";
156 | std::cout << "Invoker: Does anybody want something done after I finish?\n";
157 | if (this->on_finish_) {
158 | this->on_finish_->Execute();
159 | }
160 | }
161 | };
162 | /**
163 | * EN: The client code can parameterize an invoker with any commands.
164 | *
165 | * RU: Клиентский код может параметризовать отправителя любыми командами.
166 | */
167 |
168 | int main() {
169 | Invoker *invoker = new Invoker;
170 | invoker->SetOnStart(new SimpleCommand("Say Hi!"));
171 | Receiver *receiver = new Receiver;
172 | invoker->SetOnFinish(new ComplexCommand(receiver, "Send email", "Save report"));
173 | invoker->DoSomethingImportant();
174 |
175 | delete invoker;
176 | delete receiver;
177 |
178 | return 0;
179 | }
180 |
--------------------------------------------------------------------------------
/src/Composite/Conceptual/Output.txt:
--------------------------------------------------------------------------------
1 | Client: I've got a simple component:
2 | RESULT: Leaf
3 |
4 | Client: Now I've got a composite tree:
5 | RESULT: Branch(Branch(Leaf+Leaf)+Branch(Leaf))
6 |
7 | Client: I don't need to check the components classes even when managing the tree:
8 | RESULT: Branch(Branch(Leaf+Leaf)+Branch(Leaf)+Leaf)
9 |
10 |
--------------------------------------------------------------------------------
/src/Composite/Conceptual/main.cc:
--------------------------------------------------------------------------------
1 |
2 | #include
3 | #include
4 | #include
5 | #include
6 | /**
7 | * EN: Composite Design Pattern
8 | *
9 | * Intent: Lets you compose objects into tree structures and then work with
10 | * these structures as if they were individual objects.
11 | *
12 | * RU: Паттерн Компоновщик
13 | *
14 | * Назначение: Позволяет сгруппировать объекты в древовидную структуру, а затем
15 | * работать с ними так, как будто это единичный объект.
16 | */
17 | /**
18 | * EN: The base Component class declares common operations for both simple and
19 | * complex objects of a composition.
20 | *
21 | * RU: Базовый класс Компонент объявляет общие операции как для простых, так и
22 | * для сложных объектов структуры.
23 | */
24 | class Component {
25 | /**
26 | * @var Component
27 | */
28 | protected:
29 | Component *parent_;
30 | /**
31 | * EN: Optionally, the base Component can declare an interface for setting
32 | * and accessing a parent of the component in a tree structure. It can also
33 | * provide some default implementation for these methods.
34 | *
35 | * RU: При необходимости базовый Компонент может объявить интерфейс для
36 | * установки и получения родителя компонента в древовидной структуре. Он
37 | * также может предоставить некоторую реализацию по умолчанию для этих
38 | * методов.
39 | */
40 | public:
41 | virtual ~Component() {}
42 | void SetParent(Component *parent) {
43 | this->parent_ = parent;
44 | }
45 | Component *GetParent() const {
46 | return this->parent_;
47 | }
48 | /**
49 | * EN: In some cases, it would be beneficial to define the child-management
50 | * operations right in the base Component class. This way, you won't need to
51 | * expose any concrete component classes to the client code, even during the
52 | * object tree assembly. The downside is that these methods will be empty
53 | * for the leaf-level components.
54 | *
55 | * RU: В некоторых случаях целесообразно определить операции управления
56 | * потомками прямо в базовом классе Компонент. Таким образом, вам не нужно
57 | * будет предоставлять конкретные классы компонентов клиентскому коду, даже
58 | * во время сборки дерева объектов. Недостаток такого подхода в том, что эти
59 | * методы будут пустыми для компонентов уровня листа.
60 | */
61 | virtual void Add(Component *component) {}
62 | virtual void Remove(Component *component) {}
63 | /**
64 | * EN: You can provide a method that lets the client code figure out whether
65 | * a component can bear children.
66 | *
67 | * RU: Вы можете предоставить метод, который позволит клиентскому коду
68 | * понять, может ли компонент иметь вложенные объекты.
69 | */
70 | virtual bool IsComposite() const {
71 | return false;
72 | }
73 | /**
74 | * EN: The base Component may implement some default behavior or leave it to
75 | * concrete classes (by declaring the method containing the behavior as
76 | * "abstract").
77 | *
78 | * RU: Базовый Компонент может сам реализовать некоторое поведение по
79 | * умолчанию или поручить это конкретным классам, объявив метод, содержащий
80 | * поведение абстрактным.
81 | */
82 | virtual std::string Operation() const = 0;
83 | };
84 | /**
85 | * EN: The Leaf class represents the end objects of a composition. A leaf can't
86 | * have any children.
87 | *
88 | * Usually, it's the Leaf objects that do the actual work, whereas Composite
89 | * objects only delegate to their sub-components.
90 | *
91 | * RU: Класс Лист представляет собой конечные объекты структуры. Лист не может
92 | * иметь вложенных компонентов.
93 | *
94 | * Обычно объекты Листьев выполняют фактическую работу, тогда как объекты
95 | * Контейнера лишь делегируют работу своим подкомпонентам.
96 | */
97 | class Leaf : public Component {
98 | public:
99 | std::string Operation() const override {
100 | return "Leaf";
101 | }
102 | };
103 | /**
104 | * EN: The Composite class represents the complex components that may have
105 | * children. Usually, the Composite objects delegate the actual work to their
106 | * children and then "sum-up" the result.
107 | *
108 | * RU: Класс Контейнер содержит сложные компоненты, которые могут иметь
109 | * вложенные компоненты. Обычно объекты Контейнеры делегируют фактическую работу
110 | * своим детям, а затем «суммируют» результат.
111 | */
112 | class Composite : public Component {
113 | /**
114 | * @var \SplObjectStorage
115 | */
116 | protected:
117 | std::list children_;
118 |
119 | public:
120 | /**
121 | * EN: A composite object can add or remove other components (both simple or
122 | * complex) to or from its child list.
123 | *
124 | * RU: Объект контейнера может как добавлять компоненты в свой список
125 | * вложенных компонентов, так и удалять их, как простые, так и сложные.
126 | */
127 | void Add(Component *component) override {
128 | this->children_.push_back(component);
129 | component->SetParent(this);
130 | }
131 | /**
132 | * EN: Have in mind that this method removes the pointer to the list but doesn't frees the
133 | * memory, you should do it manually or better use smart pointers.
134 | *
135 | * RU:
136 | */
137 | void Remove(Component *component) override {
138 | children_.remove(component);
139 | component->SetParent(nullptr);
140 | }
141 | bool IsComposite() const override {
142 | return true;
143 | }
144 | /**
145 | * EN: The Composite executes its primary logic in a particular way. It
146 | * traverses recursively through all its children, collecting and summing
147 | * their results. Since the composite's children pass these calls to their
148 | * children and so forth, the whole object tree is traversed as a result.
149 | *
150 | * RU: Контейнер выполняет свою основную логику особым образом. Он проходит
151 | * рекурсивно через всех своих детей, собирая и суммируя их результаты.
152 | * Поскольку потомки контейнера передают эти вызовы своим потомкам и так
153 | * далее, в результате обходится всё дерево объектов.
154 | */
155 | std::string Operation() const override {
156 | std::string result;
157 | for (const Component *c : children_) {
158 | if (c == children_.back()) {
159 | result += c->Operation();
160 | } else {
161 | result += c->Operation() + "+";
162 | }
163 | }
164 | return "Branch(" + result + ")";
165 | }
166 | };
167 | /**
168 | * EN: The client code works with all of the components via the base interface.
169 | *
170 | * RU: Клиентский код работает со всеми компонентами через базовый интерфейс.
171 | */
172 | void ClientCode(Component *component) {
173 | // ...
174 | std::cout << "RESULT: " << component->Operation();
175 | // ...
176 | }
177 |
178 | /**
179 | * EN: Thanks to the fact that the child-management operations are declared in
180 | * the base Component class, the client code can work with any component, simple
181 | * or complex, without depending on their concrete classes.
182 | *
183 | * RU: Благодаря тому, что операции управления потомками объявлены в базовом
184 | * классе Компонента, клиентский код может работать как с простыми, так и со
185 | * сложными компонентами, вне зависимости от их конкретных классов.
186 | */
187 | void ClientCode2(Component *component1, Component *component2) {
188 | // ...
189 | if (component1->IsComposite()) {
190 | component1->Add(component2);
191 | }
192 | std::cout << "RESULT: " << component1->Operation();
193 | // ...
194 | }
195 |
196 | /**
197 | * EN: This way the client code can support the simple leaf components...
198 | *
199 | * RU: Таким образом, клиентский код может поддерживать простые
200 | * компоненты-листья...
201 | */
202 |
203 | int main() {
204 | Component *simple = new Leaf;
205 | std::cout << "Client: I've got a simple component:\n";
206 | ClientCode(simple);
207 | std::cout << "\n\n";
208 | /**
209 | * EN: ...as well as the complex composites.
210 | *
211 | * RU: ...а также сложные контейнеры.
212 | */
213 |
214 | Component *tree = new Composite;
215 | Component *branch1 = new Composite;
216 |
217 | Component *leaf_1 = new Leaf;
218 | Component *leaf_2 = new Leaf;
219 | Component *leaf_3 = new Leaf;
220 | branch1->Add(leaf_1);
221 | branch1->Add(leaf_2);
222 | Component *branch2 = new Composite;
223 | branch2->Add(leaf_3);
224 | tree->Add(branch1);
225 | tree->Add(branch2);
226 | std::cout << "Client: Now I've got a composite tree:\n";
227 | ClientCode(tree);
228 | std::cout << "\n\n";
229 |
230 | std::cout << "Client: I don't need to check the components classes even when managing the tree:\n";
231 | ClientCode2(tree, simple);
232 | std::cout << "\n";
233 |
234 | delete simple;
235 | delete tree;
236 | delete branch1;
237 | delete branch2;
238 | delete leaf_1;
239 | delete leaf_2;
240 | delete leaf_3;
241 |
242 | return 0;
243 | }
244 |
--------------------------------------------------------------------------------
/src/Decorator/Conceptual/Output.txt:
--------------------------------------------------------------------------------
1 | Client: I've got a simple component:
2 | RESULT: ConcreteComponent
3 |
4 | Client: Now I've got a decorated component:
5 | RESULT: ConcreteDecoratorB(ConcreteDecoratorA(ConcreteComponent))
6 |
7 |
--------------------------------------------------------------------------------
/src/Decorator/Conceptual/main.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | /**
5 | * EN: Decorator Design Pattern
6 | *
7 | * Intent: Lets you attach new behaviors to objects by placing these objects
8 | * inside special wrapper objects that contain the behaviors.
9 | *
10 | * RU: Паттерн Декоратор
11 | *
12 | * Назначение: Позволяет динамически добавлять объектам новую функциональность,
13 | * оборачивая их в полезные «обёртки».
14 | */
15 | /**
16 | * EN: The base Component interface defines operations that can be altered by
17 | * decorators.
18 | *
19 | * RU: Базовый интерфейс Компонента определяет поведение, которое изменяется
20 | * декораторами.
21 | */
22 | class Component {
23 | public:
24 | virtual ~Component() {}
25 | virtual std::string Operation() const = 0;
26 | };
27 | /**
28 | * EN: Concrete Components provide default implementations of the operations.
29 | * There might be several variations of these classes.
30 | *
31 | * RU: Конкретные Компоненты предоставляют реализации поведения по умолчанию.
32 | * Может быть несколько вариаций этих классов.
33 | */
34 | class ConcreteComponent : public Component {
35 | public:
36 | std::string Operation() const override {
37 | return "ConcreteComponent";
38 | }
39 | };
40 | /**
41 | * EN: The base Decorator class follows the same interface as the other
42 | * components. The primary purpose of this class is to define the wrapping
43 | * interface for all concrete decorators. The default implementation of the
44 | * wrapping code might include a field for storing a wrapped component and the
45 | * means to initialize it.
46 | *
47 | * RU: Базовый класс Декоратора следует тому же интерфейсу, что и другие
48 | * компоненты. Основная цель этого класса - определить интерфейс обёртки для
49 | * всех конкретных декораторов. Реализация кода обёртки по умолчанию может
50 | * включать в себя поле для хранения завёрнутого компонента и средства его
51 | * инициализации.
52 | */
53 | class Decorator : public Component {
54 | /**
55 | * @var Component
56 | */
57 | protected:
58 | Component* component_;
59 |
60 | public:
61 | Decorator(Component* component) : component_(component) {
62 | }
63 | /**
64 | * EN: The Decorator delegates all work to the wrapped component.
65 | *
66 | * RU: Декоратор делегирует всю работу обёрнутому компоненту.
67 | */
68 | std::string Operation() const override {
69 | return this->component_->Operation();
70 | }
71 | };
72 | /**
73 | * EN: Concrete Decorators call the wrapped object and alter its result in some
74 | * way.
75 | *
76 | * RU: Конкретные Декораторы вызывают обёрнутый объект и изменяют его результат
77 | * некоторым образом.
78 | */
79 | class ConcreteDecoratorA : public Decorator {
80 | /**
81 | * EN: Decorators may call parent implementation of the operation, instead
82 | * of calling the wrapped object directly. This approach simplifies
83 | * extension of decorator classes.
84 | *
85 | * RU: Декораторы могут вызывать родительскую реализацию операции, вместо
86 | * того, чтобы вызвать обёрнутый объект напрямую. Такой подход упрощает
87 | * расширение классов декораторов.
88 | */
89 | public:
90 | ConcreteDecoratorA(Component* component) : Decorator(component) {
91 | }
92 | std::string Operation() const override {
93 | return "ConcreteDecoratorA(" + Decorator::Operation() + ")";
94 | }
95 | };
96 | /**
97 | * EN: Decorators can execute their behavior either before or after the call to
98 | * a wrapped object.
99 | *
100 | * RU: Декораторы могут выполнять своё поведение до или после вызова обёрнутого
101 | * объекта.
102 | */
103 | class ConcreteDecoratorB : public Decorator {
104 | public:
105 | ConcreteDecoratorB(Component* component) : Decorator(component) {
106 | }
107 |
108 | std::string Operation() const override {
109 | return "ConcreteDecoratorB(" + Decorator::Operation() + ")";
110 | }
111 | };
112 | /**
113 | * EN: The client code works with all objects using the Component interface.
114 | * This way it can stay independent of the concrete classes of components it
115 | * works with.
116 | *
117 | * RU: Клиентский код работает со всеми объектами, используя интерфейс
118 | * Компонента. Таким образом, он остаётся независимым от конкретных классов
119 | * компонентов, с которыми работает.
120 | */
121 | void ClientCode(Component* component) {
122 | // ...
123 | std::cout << "RESULT: " << component->Operation();
124 | // ...
125 | }
126 |
127 | int main() {
128 | /**
129 | * EN: This way the client code can support both simple components...
130 | *
131 | * RU: Таким образом, клиентский код может поддерживать как простые
132 | * компоненты...
133 | */
134 | Component* simple = new ConcreteComponent;
135 | std::cout << "Client: I've got a simple component:\n";
136 | ClientCode(simple);
137 | std::cout << "\n\n";
138 | /**
139 | * EN: ...as well as decorated ones.
140 | *
141 | * Note how decorators can wrap not only simple components but the other
142 | * decorators as well.
143 | *
144 | * RU: ...так и декорированные.
145 | *
146 | * Обратите внимание, что декораторы могут обёртывать не только простые
147 | * компоненты, но и другие декораторы.
148 | */
149 | Component* decorator1 = new ConcreteDecoratorA(simple);
150 | Component* decorator2 = new ConcreteDecoratorB(decorator1);
151 | std::cout << "Client: Now I've got a decorated component:\n";
152 | ClientCode(decorator2);
153 | std::cout << "\n";
154 |
155 | delete simple;
156 | delete decorator1;
157 | delete decorator2;
158 |
159 | return 0;
160 | }
--------------------------------------------------------------------------------
/src/Facade/Conceptual/Output.txt:
--------------------------------------------------------------------------------
1 | Facade initializes subsystems:
2 | Subsystem1: Ready!
3 | Subsystem2: Get ready!
4 | Facade orders subsystems to perform the action:
5 | Subsystem1: Go!
6 | Subsystem2: Fire!
7 |
8 |
--------------------------------------------------------------------------------
/src/Facade/Conceptual/main.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | /**
5 | * EN: Facade Design Pattern
6 | *
7 | * Intent: Provides a simplified interface to a library, a framework, or any
8 | * other complex set of classes.
9 | *
10 | * RU: Паттерн Фасад
11 | *
12 | * Назначение: Предоставляет простой интерфейс к сложной системе классов,
13 | * библиотеке или фреймворку.
14 | */
15 |
16 | /**
17 | * EN: The Subsystem can accept requests either from the facade or client
18 | * directly. In any case, to the Subsystem, the Facade is yet another client,
19 | * and it's not a part of the Subsystem.
20 | *
21 | * RU: Подсистема может принимать запросы либо от фасада, либо от клиента
22 | * напрямую. В любом случае, для Подсистемы Фасад – это еще один клиент, и он не
23 | * является частью Подсистемы.
24 | */
25 | class Subsystem1 {
26 | public:
27 | std::string Operation1() const {
28 | return "Subsystem1: Ready!\n";
29 | }
30 | // ...
31 | std::string OperationN() const {
32 | return "Subsystem1: Go!\n";
33 | }
34 | };
35 | /**
36 | * EN: Some facades can work with multiple subsystems at the same time.
37 | *
38 | * RU: Некоторые фасады могут работать с разными подсистемами одновременно.
39 | */
40 | class Subsystem2 {
41 | public:
42 | std::string Operation1() const {
43 | return "Subsystem2: Get ready!\n";
44 | }
45 | // ...
46 | std::string OperationZ() const {
47 | return "Subsystem2: Fire!\n";
48 | }
49 | };
50 |
51 | /**
52 | * EN: The Facade class provides a simple interface to the complex logic of one
53 | * or several subsystems. The Facade delegates the client requests to the
54 | * appropriate objects within the subsystem. The Facade is also responsible for
55 | * managing their lifecycle. All of this shields the client from the undesired
56 | * complexity of the subsystem.
57 | *
58 | * RU: Класс Фасада предоставляет простой интерфейс для сложной логики одной или
59 | * нескольких подсистем. Фасад делегирует запросы клиентов соответствующим
60 | * объектам внутри подсистемы. Фасад также отвечает за управление их жизненным
61 | * циклом. Все это защищает клиента от нежелательной сложности подсистемы.
62 | */
63 | class Facade {
64 | protected:
65 | Subsystem1 *subsystem1_;
66 | Subsystem2 *subsystem2_;
67 | /**
68 | * EN: Depending on your application's needs, you can provide the Facade
69 | * with existing subsystem objects or force the Facade to create them on its
70 | * own.
71 | *
72 | * RU: В зависимости от потребностей вашего приложения вы можете
73 | * предоставить Фасаду существующие объекты подсистемы или заставить Фасад
74 | * создать их самостоятельно.
75 | */
76 | public:
77 | /**
78 | * EN: In this case we will delegate the memory ownership to Facade Class
79 | *
80 | * RU:
81 | */
82 | Facade(
83 | Subsystem1 *subsystem1 = nullptr,
84 | Subsystem2 *subsystem2 = nullptr) {
85 | this->subsystem1_ = subsystem1 ?: new Subsystem1;
86 | this->subsystem2_ = subsystem2 ?: new Subsystem2;
87 | }
88 | ~Facade() {
89 | delete subsystem1_;
90 | delete subsystem2_;
91 | }
92 | /**
93 | * EN: The Facade's methods are convenient shortcuts to the sophisticated
94 | * functionality of the subsystems. However, clients get only to a fraction
95 | * of a subsystem's capabilities.
96 | *
97 | * RU: Методы Фасада удобны для быстрого доступа к сложной функциональности
98 | * подсистем. Однако клиенты получают только часть возможностей подсистемы.
99 | */
100 | std::string Operation() {
101 | std::string result = "Facade initializes subsystems:\n";
102 | result += this->subsystem1_->Operation1();
103 | result += this->subsystem2_->Operation1();
104 | result += "Facade orders subsystems to perform the action:\n";
105 | result += this->subsystem1_->OperationN();
106 | result += this->subsystem2_->OperationZ();
107 | return result;
108 | }
109 | };
110 |
111 | /**
112 | * EN: The client code works with complex subsystems through a simple interface
113 | * provided by the Facade. When a facade manages the lifecycle of the subsystem,
114 | * the client might not even know about the existence of the subsystem. This
115 | * approach lets you keep the complexity under control.
116 | *
117 | * RU: Клиентский код работает со сложными подсистемами через простой интерфейс,
118 | * предоставляемый Фасадом. Когда фасад управляет жизненным циклом подсистемы,
119 | * клиент может даже не знать о существовании подсистемы. Такой подход позволяет
120 | * держать сложность под контролем.
121 | */
122 | void ClientCode(Facade *facade) {
123 | // ...
124 | std::cout << facade->Operation();
125 | // ...
126 | }
127 | /**
128 | * EN: The client code may have some of the subsystem's objects already created.
129 | * In this case, it might be worthwhile to initialize the Facade with these
130 | * objects instead of letting the Facade create new instances.
131 | *
132 | * RU: В клиентском коде могут быть уже созданы некоторые объекты подсистемы. В
133 | * этом случае может оказаться целесообразным инициализировать Фасад с этими
134 | * объектами вместо того, чтобы позволить Фасаду создавать новые экземпляры.
135 | */
136 |
137 | int main() {
138 | Subsystem1 *subsystem1 = new Subsystem1;
139 | Subsystem2 *subsystem2 = new Subsystem2;
140 | Facade *facade = new Facade(subsystem1, subsystem2);
141 | ClientCode(facade);
142 |
143 | delete facade;
144 |
145 | return 0;
146 | }
147 |
--------------------------------------------------------------------------------
/src/FactoryMethod/Conceptual/Output.txt:
--------------------------------------------------------------------------------
1 | App: Launched with the ConcreteCreator1.
2 | Client: I'm not aware of the creator's class, but it still works.
3 | Creator: The same creator's code has just worked with {Result of the ConcreteProduct1}
4 |
5 | App: Launched with the ConcreteCreator2.
6 | Client: I'm not aware of the creator's class, but it still works.
7 | Creator: The same creator's code has just worked with {Result of the ConcreteProduct2}
8 |
9 |
--------------------------------------------------------------------------------
/src/FactoryMethod/Conceptual/main.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | /**
5 | * EN: Factory Method Design Pattern
6 | *
7 | * Intent: Provides an interface for creating objects in a superclass, but
8 | * allows subclasses to alter the type of objects that will be created.
9 | *
10 | * RU: Паттерн Фабричный Метод
11 | *
12 | * Назначение: Определяет общий интерфейс для создания объектов в суперклассе,
13 | * позволяя подклассам изменять тип создаваемых объектов.
14 | */
15 |
16 | /**
17 | * EN: The Product interface declares the operations that all concrete products
18 | * must implement.
19 | *
20 | * RU: Интерфейс Продукта объявляет операции, которые должны выполнять все
21 | * конкретные продукты.
22 | */
23 |
24 | class Product {
25 | public:
26 | virtual ~Product() {}
27 | virtual std::string Operation() const = 0;
28 | };
29 |
30 | /**
31 | * EN: Concrete Products provide various implementations of the Product
32 | * interface.
33 | *
34 | * RU: Конкретные Продукты предоставляют различные реализации интерфейса
35 | * Продукта.
36 | */
37 | class ConcreteProduct1 : public Product {
38 | public:
39 | std::string Operation() const override {
40 | return "{Result of the ConcreteProduct1}";
41 | }
42 | };
43 | class ConcreteProduct2 : public Product {
44 | public:
45 | std::string Operation() const override {
46 | return "{Result of the ConcreteProduct2}";
47 | }
48 | };
49 |
50 | /**
51 | * EN: The Creator class declares the factory method that is supposed to return
52 | * an object of a Product class. The Creator's subclasses usually provide the
53 | * implementation of this method.
54 | *
55 | * RU: Класс Создатель объявляет фабричный метод, который должен возвращать
56 | * объект класса Продукт. Подклассы Создателя обычно предоставляют реализацию
57 | * этого метода.
58 | */
59 |
60 | class Creator {
61 | /**
62 | * EN: Note that the Creator may also provide some default implementation of
63 | * the factory method.
64 | *
65 | * RU: Обратите внимание, что Создатель может также обеспечить реализацию
66 | * фабричного метода по умолчанию.
67 | */
68 | public:
69 | virtual ~Creator(){};
70 | virtual Product* FactoryMethod() const = 0;
71 | /**
72 | * EN: Also note that, despite its name, the Creator's primary
73 | * responsibility is not creating products. Usually, it contains some core
74 | * business logic that relies on Product objects, returned by the factory
75 | * method. Subclasses can indirectly change that business logic by
76 | * overriding the factory method and returning a different type of product
77 | * from it.
78 | *
79 | * RU: Также заметьте, что, несмотря на название, основная обязанность
80 | * Создателя не заключается в создании продуктов. Обычно он содержит
81 | * некоторую базовую бизнес-логику, которая основана на объектах Продуктов,
82 | * возвращаемых фабричным методом. Подклассы могут косвенно изменять эту
83 | * бизнес-логику, переопределяя фабричный метод и возвращая из него другой
84 | * тип продукта.
85 | */
86 |
87 | std::string SomeOperation() const {
88 | // EN: Call the factory method to create a Product object.
89 | //
90 | // RU: Вызываем фабричный метод, чтобы получить объект-продукт.
91 | Product* product = this->FactoryMethod();
92 | // EN: Now, use the product.
93 | //
94 | // RU: Далее, работаем с этим продуктом.
95 | std::string result = "Creator: The same creator's code has just worked with " + product->Operation();
96 | delete product;
97 | return result;
98 | }
99 | };
100 |
101 | /**
102 | * EN: Concrete Creators override the factory method in order to change the
103 | * resulting product's type.
104 | *
105 | * RU: Конкретные Создатели переопределяют фабричный метод для того, чтобы
106 | * изменить тип результирующего продукта.
107 | */
108 | class ConcreteCreator1 : public Creator {
109 | /**
110 | * EN: Note that the signature of the method still uses the abstract product
111 | * type, even though the concrete product is actually returned from the
112 | * method. This way the Creator can stay independent of concrete product
113 | * classes.
114 | *
115 | * RU: Обратите внимание, что сигнатура метода по-прежнему использует тип
116 | * абстрактного продукта, хотя фактически из метода возвращается конкретный
117 | * продукт. Таким образом, Создатель может оставаться независимым от
118 | * конкретных классов продуктов.
119 | */
120 | public:
121 | Product* FactoryMethod() const override {
122 | return new ConcreteProduct1();
123 | }
124 | };
125 |
126 | class ConcreteCreator2 : public Creator {
127 | public:
128 | Product* FactoryMethod() const override {
129 | return new ConcreteProduct2();
130 | }
131 | };
132 |
133 | /**
134 | * EN: The client code works with an instance of a concrete creator, albeit
135 | * through its base interface. As long as the client keeps working with the
136 | * creator via the base interface, you can pass it any creator's subclass.
137 | *
138 | * RU: Клиентский код работает с экземпляром конкретного создателя, хотя и через
139 | * его базовый интерфейс. Пока клиент продолжает работать с создателем через
140 | * базовый интерфейс, вы можете передать ему любой подкласс создателя.
141 | */
142 | void ClientCode(const Creator& creator) {
143 | // ...
144 | std::cout << "Client: I'm not aware of the creator's class, but it still works.\n"
145 | << creator.SomeOperation() << std::endl;
146 | // ...
147 | }
148 |
149 | /**
150 | * EN: The Application picks a creator's type depending on the configuration or
151 | * environment.
152 | *
153 | * RU: Приложение выбирает тип создателя в зависимости от конфигурации или
154 | * среды.
155 | */
156 |
157 | int main() {
158 | std::cout << "App: Launched with the ConcreteCreator1.\n";
159 | Creator* creator = new ConcreteCreator1();
160 | ClientCode(*creator);
161 | std::cout << std::endl;
162 | std::cout << "App: Launched with the ConcreteCreator2.\n";
163 | Creator* creator2 = new ConcreteCreator2();
164 | ClientCode(*creator2);
165 |
166 | delete creator;
167 | delete creator2;
168 | return 0;
169 | }
--------------------------------------------------------------------------------
/src/Flyweight/Conceptual/Output.txt:
--------------------------------------------------------------------------------
1 | FlyweightFactory: I have 5 flyweights:
2 | BMW_X6_white
3 | Mercedes Benz_C500_red
4 | Mercedes Benz_C300_black
5 | BMW_M5_red
6 | Chevrolet_Camaro2018_pink
7 |
8 | Client: Adding a car to database.
9 | FlyweightFactory: Reusing existing flyweight.
10 | Flyweight: Displaying shared ([ BMW , M5 , red ]) and unique ([ CL234IR , James Doe ]) state.
11 |
12 | Client: Adding a car to database.
13 | FlyweightFactory: Can't find a flyweight, creating new one.
14 | Flyweight: Displaying shared ([ BMW , X1 , red ]) and unique ([ CL234IR , James Doe ]) state.
15 |
16 | FlyweightFactory: I have 6 flyweights:
17 | BMW_X1_red
18 | Mercedes Benz_C300_black
19 | BMW_X6_white
20 | Mercedes Benz_C500_red
21 | BMW_M5_red
22 | Chevrolet_Camaro2018_pink
--------------------------------------------------------------------------------
/src/Flyweight/Conceptual/main.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | /**
7 | * EN: Flyweight Design Pattern
8 | *
9 | * Intent: Lets you fit more objects into the available amount of RAM by sharing
10 | * common parts of state between multiple objects, instead of keeping all of the
11 | * data in each object.
12 | *
13 | * RU: Паттерн Легковес
14 | *
15 | * Назначение: Позволяет вместить бóльшее количество объектов в отведённую
16 | * оперативную память. Легковес экономит память, разделяя общее состояние
17 | * объектов между собой, вместо хранения одинаковых данных в каждом объекте.
18 | */
19 |
20 | struct SharedState
21 | {
22 | std::string brand_;
23 | std::string model_;
24 | std::string color_;
25 |
26 | SharedState(const std::string &brand, const std::string &model, const std::string &color)
27 | : brand_(brand), model_(model), color_(color)
28 | {
29 | }
30 |
31 | friend std::ostream &operator<<(std::ostream &os, const SharedState &ss)
32 | {
33 | return os << "[ " << ss.brand_ << " , " << ss.model_ << " , " << ss.color_ << " ]";
34 | }
35 | };
36 |
37 | struct UniqueState
38 | {
39 | std::string owner_;
40 | std::string plates_;
41 |
42 | UniqueState(const std::string &owner, const std::string &plates)
43 | : owner_(owner), plates_(plates)
44 | {
45 | }
46 |
47 | friend std::ostream &operator<<(std::ostream &os, const UniqueState &us)
48 | {
49 | return os << "[ " << us.owner_ << " , " << us.plates_ << " ]";
50 | }
51 | };
52 |
53 | /**
54 | * EN: The Flyweight stores a common portion of the state (also called intrinsic
55 | * state) that belongs to multiple real business entities. The Flyweight accepts
56 | * the rest of the state (extrinsic state, unique for each entity) via its
57 | * method parameters.
58 | *
59 | * RU: Легковес хранит общую часть состояния (также называемую внутренним
60 | * состоянием), которая принадлежит нескольким реальным бизнес-объектам.
61 | * Легковес принимает оставшуюся часть состояния (внешнее состояние, уникальное
62 | * для каждого объекта) через его параметры метода.
63 | */
64 | class Flyweight
65 | {
66 | private:
67 | SharedState *shared_state_;
68 |
69 | public:
70 | Flyweight(const SharedState *shared_state) : shared_state_(new SharedState(*shared_state))
71 | {
72 | }
73 | Flyweight(const Flyweight &other) : shared_state_(new SharedState(*other.shared_state_))
74 | {
75 | }
76 | ~Flyweight()
77 | {
78 | delete shared_state_;
79 | }
80 | SharedState *shared_state() const
81 | {
82 | return shared_state_;
83 | }
84 | void Operation(const UniqueState &unique_state) const
85 | {
86 | std::cout << "Flyweight: Displaying shared (" << *shared_state_ << ") and unique (" << unique_state << ") state.\n";
87 | }
88 | };
89 | /**
90 | * EN: The Flyweight Factory creates and manages the Flyweight objects. It
91 | * ensures that flyweights are shared correctly. When the client requests a
92 | * flyweight, the factory either returns an existing instance or creates a new
93 | * one, if it doesn't exist yet.
94 | *
95 | * RU: Фабрика Легковесов создает объекты-Легковесы и управляет ими. Она
96 | * обеспечивает правильное разделение легковесов. Когда клиент запрашивает
97 | * легковес, фабрика либо возвращает существующий экземпляр, либо создает новый,
98 | * если он ещё не существует.
99 | */
100 | class FlyweightFactory
101 | {
102 | /**
103 | * @var Flyweight[]
104 | */
105 | private:
106 | std::unordered_map flyweights_;
107 | /**
108 | * EN: Returns a Flyweight's string hash for a given state.
109 | *
110 | * RU: Возвращает хеш строки Легковеса для данного состояния.
111 | */
112 | std::string GetKey(const SharedState &ss) const
113 | {
114 | return ss.brand_ + "_" + ss.model_ + "_" + ss.color_;
115 | }
116 |
117 | public:
118 | FlyweightFactory(std::initializer_list share_states)
119 | {
120 | for (const SharedState &ss : share_states)
121 | {
122 | this->flyweights_.insert(std::make_pair(this->GetKey(ss), Flyweight(&ss)));
123 | }
124 | }
125 |
126 | /**
127 | * EN: Returns an existing Flyweight with a given state or creates a new
128 | * one.
129 | *
130 | * RU: Возвращает существующий Легковес с заданным состоянием или создает
131 | * новый.
132 | */
133 | Flyweight GetFlyweight(const SharedState &shared_state)
134 | {
135 | std::string key = this->GetKey(shared_state);
136 | if (this->flyweights_.find(key) == this->flyweights_.end())
137 | {
138 | std::cout << "FlyweightFactory: Can't find a flyweight, creating new one.\n";
139 | this->flyweights_.insert(std::make_pair(key, Flyweight(&shared_state)));
140 | }
141 | else
142 | {
143 | std::cout << "FlyweightFactory: Reusing existing flyweight.\n";
144 | }
145 | return this->flyweights_.at(key);
146 | }
147 | void ListFlyweights() const
148 | {
149 | size_t count = this->flyweights_.size();
150 | std::cout << "\nFlyweightFactory: I have " << count << " flyweights:\n";
151 | for (std::pair pair : this->flyweights_)
152 | {
153 | std::cout << pair.first << "\n";
154 | }
155 | }
156 | };
157 |
158 | // ...
159 | void AddCarToPoliceDatabase(
160 | FlyweightFactory &ff, const std::string &plates, const std::string &owner,
161 | const std::string &brand, const std::string &model, const std::string &color)
162 | {
163 | std::cout << "\nClient: Adding a car to database.\n";
164 | const Flyweight &flyweight = ff.GetFlyweight({brand, model, color});
165 | // EN: The client code either stores or calculates extrinsic state and
166 | // passes it to the flyweight's methods.
167 | //
168 | // RU: Клиентский код либо сохраняет, либо вычисляет внешнее состояние и
169 | // передает его методам легковеса.
170 | flyweight.Operation({owner, plates});
171 | }
172 |
173 | /**
174 | * EN: The client code usually creates a bunch of pre-populated flyweights in
175 | * the initialization stage of the application.
176 | *
177 | * RU: Клиентский код обычно создает кучу предварительно заполненных легковесов
178 | * на этапе инициализации приложения.
179 | */
180 |
181 | int main()
182 | {
183 | FlyweightFactory *factory = new FlyweightFactory({{"Chevrolet", "Camaro2018", "pink"}, {"Mercedes Benz", "C300", "black"}, {"Mercedes Benz", "C500", "red"}, {"BMW", "M5", "red"}, {"BMW", "X6", "white"}});
184 | factory->ListFlyweights();
185 |
186 | AddCarToPoliceDatabase(*factory,
187 | "CL234IR",
188 | "James Doe",
189 | "BMW",
190 | "M5",
191 | "red");
192 |
193 | AddCarToPoliceDatabase(*factory,
194 | "CL234IR",
195 | "James Doe",
196 | "BMW",
197 | "X1",
198 | "red");
199 | factory->ListFlyweights();
200 | delete factory;
201 |
202 | return 0;
203 | }
204 |
--------------------------------------------------------------------------------
/src/Iterator/Conceptual/Output.txt:
--------------------------------------------------------------------------------
1 | ________________Iterator with int______________________________________
2 | 0
3 | 1
4 | 2
5 | 3
6 | 4
7 | 5
8 | 6
9 | 7
10 | 8
11 | 9
12 | ________________Iterator with custom Class______________________________
13 | 100
14 | 1000
15 | 10000
--------------------------------------------------------------------------------
/src/Iterator/Conceptual/main.cc:
--------------------------------------------------------------------------------
1 | /**
2 | * EN: Iterator Design Pattern
3 | *
4 | * Intent: Lets you traverse elements of a collection without exposing its
5 | * underlying representation (list, stack, tree, etc.).
6 | *
7 | * RU: Паттерн Итератор
8 | *
9 | * Назначение: Даёт возможность последовательно обходить элементы составных
10 | * объектов, не раскрывая их внутреннего представления.
11 | */
12 |
13 | #include
14 | #include
15 | #include
16 |
17 | /**
18 | * EN: C++ has its own implementation of iterator that works with
19 | * a different generics containers defined by the standard library.
20 | *
21 | * RU:
22 | */
23 |
24 | template
25 | class Iterator {
26 | public:
27 | typedef typename std::vector::iterator iter_type;
28 | Iterator(U *p_data, bool reverse = false) : m_p_data_(p_data) {
29 | m_it_ = m_p_data_->m_data_.begin();
30 | }
31 |
32 | void First() {
33 | m_it_ = m_p_data_->m_data_.begin();
34 | }
35 |
36 | void Next() {
37 | m_it_++;
38 | }
39 |
40 | bool IsDone() {
41 | return (m_it_ == m_p_data_->m_data_.end());
42 | }
43 |
44 | iter_type Current() {
45 | return m_it_;
46 | }
47 |
48 | private:
49 | U *m_p_data_;
50 | iter_type m_it_;
51 | };
52 |
53 | /**
54 | * EN: Generic Collections/Containers provides one or several methods for retrieving fresh
55 | * iterator instances, compatible with the collection class.
56 | *
57 | * RU: Конкретные Коллекции предоставляют один или несколько методов для
58 | * получения новых экземпляров итератора, совместимых с классом коллекции.
59 | */
60 |
61 | template
62 | class Container {
63 | friend class Iterator;
64 |
65 | public:
66 | void Add(T a) {
67 | m_data_.push_back(a);
68 | }
69 |
70 | Iterator *CreateIterator() {
71 | return new Iterator(this);
72 | }
73 |
74 | private:
75 | std::vector m_data_;
76 | };
77 |
78 | class Data {
79 | public:
80 | Data(int a = 0) : m_data_(a) {}
81 |
82 | void set_data(int a) {
83 | m_data_ = a;
84 | }
85 |
86 | int data() {
87 | return m_data_;
88 | }
89 |
90 | private:
91 | int m_data_;
92 | };
93 |
94 | /**
95 | * EN: The client code may or may not know about the Concrete Iterator or
96 | * Collection classes, for this implementation the container is generic so you
97 | * can used with an int or with a custom class.
98 | *
99 | * RU:
100 | */
101 | void ClientCode() {
102 | std::cout << "________________Iterator with int______________________________________" << std::endl;
103 | Container cont;
104 |
105 | for (int i = 0; i < 10; i++) {
106 | cont.Add(i);
107 | }
108 |
109 | Iterator> *it = cont.CreateIterator();
110 | for (it->First(); !it->IsDone(); it->Next()) {
111 | std::cout << *it->Current() << std::endl;
112 | }
113 |
114 | Container cont2;
115 | Data a(100), b(1000), c(10000);
116 | cont2.Add(a);
117 | cont2.Add(b);
118 | cont2.Add(c);
119 |
120 | std::cout << "________________Iterator with custom Class______________________________" << std::endl;
121 | Iterator> *it2 = cont2.CreateIterator();
122 | for (it2->First(); !it2->IsDone(); it2->Next()) {
123 | std::cout << it2->Current()->data() << std::endl;
124 | }
125 | delete it;
126 | delete it2;
127 | }
128 |
129 | int main() {
130 | ClientCode();
131 | return 0;
132 | }
133 |
--------------------------------------------------------------------------------
/src/Mediator/Conceptual/Output.txt:
--------------------------------------------------------------------------------
1 | Client triggers operation A.
2 | Component 1 does A.
3 | Mediator reacts on A and triggers following operations:
4 | Component 2 does C.
5 |
6 | Client triggers operation D.
7 | Component 2 does D.
8 | Mediator reacts on D and triggers following operations:
9 | Component 1 does B.
10 | Component 2 does C.
--------------------------------------------------------------------------------
/src/Mediator/Conceptual/main.cc:
--------------------------------------------------------------------------------
1 |
2 | #include
3 | #include
4 | /**
5 | * EN: Mediator Design Pattern
6 | *
7 | * Intent: Lets you reduce chaotic dependencies between objects. The pattern
8 | * restricts direct communications between the objects and forces them to
9 | * collaborate only via a mediator object.
10 | *
11 | * RU: Паттерн Посредник
12 | *
13 | * Назначение: Позволяет уменьшить связанность множества классов между собой,
14 | * благодаря перемещению этих связей в один класс-посредник.
15 | */
16 |
17 | /**
18 | * EN: The Mediator interface declares a method used by components to notify the
19 | * mediator about various events. The Mediator may react to these events and
20 | * pass the execution to other components.
21 | *
22 | * RU: Интерфейс Посредника предоставляет метод, используемый компонентами для
23 | * уведомления посредника о различных событиях. Посредник может реагировать на
24 | * эти события и передавать исполнение другим компонентам.
25 | */
26 | class BaseComponent;
27 | class Mediator {
28 | public:
29 | virtual void Notify(BaseComponent *sender, std::string event) const = 0;
30 | };
31 |
32 | /**
33 | * EN: The Base Component provides the basic functionality of storing a
34 | * mediator's instance inside component objects.
35 | *
36 | * RU: Базовый Компонент обеспечивает базовую функциональность хранения
37 | * экземпляра посредника внутри объектов компонентов.
38 | */
39 | class BaseComponent {
40 | protected:
41 | Mediator *mediator_;
42 |
43 | public:
44 | BaseComponent(Mediator *mediator = nullptr) : mediator_(mediator) {
45 | }
46 | void set_mediator(Mediator *mediator) {
47 | this->mediator_ = mediator;
48 | }
49 | };
50 |
51 | /**
52 | * EN: Concrete Components implement various functionality. They don't depend on
53 | * other components. They also don't depend on any concrete mediator classes.
54 | *
55 | * RU: Конкретные Компоненты реализуют различную функциональность. Они не
56 | * зависят от других компонентов. Они также не зависят от каких-либо конкретных
57 | * классов посредников.
58 | */
59 | class Component1 : public BaseComponent {
60 | public:
61 | void DoA() {
62 | std::cout << "Component 1 does A.\n";
63 | this->mediator_->Notify(this, "A");
64 | }
65 | void DoB() {
66 | std::cout << "Component 1 does B.\n";
67 | this->mediator_->Notify(this, "B");
68 | }
69 | };
70 |
71 | class Component2 : public BaseComponent {
72 | public:
73 | void DoC() {
74 | std::cout << "Component 2 does C.\n";
75 | this->mediator_->Notify(this, "C");
76 | }
77 | void DoD() {
78 | std::cout << "Component 2 does D.\n";
79 | this->mediator_->Notify(this, "D");
80 | }
81 | };
82 |
83 | /**
84 | * EN: Concrete Mediators implement cooperative behavior by coordinating several
85 | * components.
86 | *
87 | * RU: Конкретные Посредники реализуют совместное поведение, координируя
88 | * отдельные компоненты.
89 | */
90 | class ConcreteMediator : public Mediator {
91 | private:
92 | Component1 *component1_;
93 | Component2 *component2_;
94 |
95 | public:
96 | ConcreteMediator(Component1 *c1, Component2 *c2) : component1_(c1), component2_(c2) {
97 | this->component1_->set_mediator(this);
98 | this->component2_->set_mediator(this);
99 | }
100 | void Notify(BaseComponent *sender, std::string event) const override {
101 | if (event == "A") {
102 | std::cout << "Mediator reacts on A and triggers following operations:\n";
103 | this->component2_->DoC();
104 | }
105 | if (event == "D") {
106 | std::cout << "Mediator reacts on D and triggers following operations:\n";
107 | this->component1_->DoB();
108 | this->component2_->DoC();
109 | }
110 | }
111 | };
112 |
113 | /**
114 | * EN: The client code.
115 | *
116 | * RU: Клиентский код.
117 | */
118 |
119 | void ClientCode() {
120 | Component1 *c1 = new Component1;
121 | Component2 *c2 = new Component2;
122 | ConcreteMediator *mediator = new ConcreteMediator(c1, c2);
123 | std::cout << "Client triggers operation A.\n";
124 | c1->DoA();
125 | std::cout << "\n";
126 | std::cout << "Client triggers operation D.\n";
127 | c2->DoD();
128 |
129 | delete c1;
130 | delete c2;
131 | delete mediator;
132 | }
133 |
134 | int main() {
135 | ClientCode();
136 | return 0;
137 | }
138 |
--------------------------------------------------------------------------------
/src/Memento/Conceptual/Output.txt:
--------------------------------------------------------------------------------
1 | Originator: My initial state is: Super-duper-super-puper-super.
2 |
3 | Caretaker: Saving Originator's state...
4 | Originator: I'm doing something important.
5 | Originator: and my state has changed to: uOInE8wmckHYPwZS7PtUTwuwZfCIbz
6 |
7 | Caretaker: Saving Originator's state...
8 | Originator: I'm doing something important.
9 | Originator: and my state has changed to: te6RGmykRpbqaWo5MEwjji1fpM1t5D
10 |
11 | Caretaker: Saving Originator's state...
12 | Originator: I'm doing something important.
13 | Originator: and my state has changed to: hX5xWDVljcQ9ydD7StUfbBt5Z7pcSN
14 |
15 | Caretaker: Here's the list of mementos:
16 | Sat Oct 19 18:09:37 2019
17 | / (Super-dup...)
18 | Sat Oct 19 18:09:37 2019
19 | / (uOInE8wmc...)
20 | Sat Oct 19 18:09:37 2019
21 | / (te6RGmykR...)
22 |
23 | Client: Now, let's rollback!
24 |
25 | Caretaker: Restoring state to: Sat Oct 19 18:09:37 2019
26 | / (te6RGmykR...)
27 | Originator: My state has changed to: te6RGmykRpbqaWo5MEwjji1fpM1t5D
28 |
29 | Client: Once more!
30 |
31 | Caretaker: Restoring state to: Sat Oct 19 18:09:37 2019
32 | / (uOInE8wmc...)
33 | Originator: My state has changed to: uOInE8wmckHYPwZS7PtUTwuwZfCIbz
--------------------------------------------------------------------------------
/src/Memento/Conceptual/main.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | /**
8 | * EN: Memento Design Pattern
9 | *
10 | * Intent: Lets you save and restore the previous state of an object without
11 | * revealing the details of its implementation.
12 | *
13 | * RU: Паттерн Снимок
14 | *
15 | * Назначение: Фиксирует и восстанавливает внутреннее состояние объекта таким
16 | * образом, чтобы в дальнейшем объект можно было восстановить в этом состоянии
17 | * без нарушения инкапсуляции.
18 | */
19 |
20 | /**
21 | * EN: The Memento interface provides a way to retrieve the memento's metadata,
22 | * such as creation date or name. However, it doesn't expose the Originator's
23 | * state.
24 | *
25 | * RU: Интерфейс Снимка предоставляет способ извлечения метаданных снимка, таких
26 | * как дата создания или название. Однако он не раскрывает состояние Создателя.
27 | */
28 | class Memento {
29 | public:
30 | virtual ~Memento() {}
31 | virtual std::string GetName() const = 0;
32 | virtual std::string date() const = 0;
33 | virtual std::string state() const = 0;
34 | };
35 |
36 | /**
37 | * EN: The Concrete Memento contains the infrastructure for storing the
38 | * Originator's state.
39 | *
40 | * RU: Конкретный снимок содержит инфраструктуру для хранения состояния
41 | * Создателя.
42 | */
43 | class ConcreteMemento : public Memento {
44 | private:
45 | std::string state_;
46 | std::string date_;
47 |
48 | public:
49 | ConcreteMemento(std::string state) : state_(state) {
50 | this->state_ = state;
51 | std::time_t now = std::time(0);
52 | this->date_ = std::ctime(&now);
53 | }
54 | /**
55 | * EN: The Originator uses this method when restoring its state.
56 | *
57 | * RU: Создатель использует этот метод, когда восстанавливает своё
58 | * состояние.
59 | */
60 | std::string state() const override {
61 | return this->state_;
62 | }
63 | /**
64 | * EN: The rest of the methods are used by the Caretaker to display
65 | * metadata.
66 | *
67 | * RU: Остальные методы используются Опекуном для отображения метаданных.
68 | */
69 | std::string GetName() const override {
70 | return this->date_ + " / (" + this->state_.substr(0, 9) + "...)";
71 | }
72 | std::string date() const override {
73 | return this->date_;
74 | }
75 | };
76 |
77 | /**
78 | * EN: The Originator holds some important state that may change over time. It
79 | * also defines a method for saving the state inside a memento and another
80 | * method for restoring the state from it.
81 | *
82 | * RU: Создатель содержит некоторое важное состояние, которое может со временем
83 | * меняться. Он также объявляет метод сохранения состояния внутри снимка и метод
84 | * восстановления состояния из него.
85 | */
86 | class Originator {
87 | /**
88 | * EN: @var string For the sake of simplicity, the originator's state is
89 | * stored inside a single variable.
90 | *
91 | * RU: @var string Для удобства состояние создателя хранится внутри одной
92 | * переменной.
93 | */
94 | private:
95 | std::string state_;
96 |
97 | std::string GenerateRandomString(int length = 10) {
98 | const char alphanum[] =
99 | "0123456789"
100 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
101 | "abcdefghijklmnopqrstuvwxyz";
102 | int stringLength = sizeof(alphanum) - 1;
103 |
104 | std::string random_string;
105 | for (int i = 0; i < length; i++) {
106 | random_string += alphanum[std::rand() % stringLength];
107 | }
108 | return random_string;
109 | }
110 |
111 | public:
112 | Originator(std::string state) : state_(state) {
113 | std::cout << "Originator: My initial state is: " << this->state_ << "\n";
114 | }
115 | /**
116 | * EN: The Originator's business logic may affect its internal state.
117 | * Therefore, the client should backup the state before launching methods of
118 | * the business logic via the save() method.
119 | *
120 | * RU: Бизнес-логика Создателя может повлиять на его внутреннее состояние.
121 | * Поэтому клиент должен выполнить резервное копирование состояния с помощью
122 | * метода save перед запуском методов бизнес-логики.
123 | */
124 | void DoSomething() {
125 | std::cout << "Originator: I'm doing something important.\n";
126 | this->state_ = this->GenerateRandomString(30);
127 | std::cout << "Originator: and my state has changed to: " << this->state_ << "\n";
128 | }
129 |
130 | /**
131 | * EN: Saves the current state inside a memento.
132 | *
133 | * RU: Сохраняет текущее состояние внутри снимка.
134 | */
135 | Memento *Save() {
136 | return new ConcreteMemento(this->state_);
137 | }
138 | /**
139 | * EN: Restores the Originator's state from a memento object.
140 | *
141 | * RU: Восстанавливает состояние Создателя из объекта снимка.
142 | */
143 | void Restore(Memento *memento) {
144 | this->state_ = memento->state();
145 | std::cout << "Originator: My state has changed to: " << this->state_ << "\n";
146 | delete memento;
147 | }
148 | };
149 |
150 | /**
151 | * EN: The Caretaker doesn't depend on the Concrete Memento class. Therefore, it
152 | * doesn't have access to the originator's state, stored inside the memento. It
153 | * works with all mementos via the base Memento interface.
154 | *
155 | * RU: Опекун не зависит от класса Конкретного Снимка. Таким образом, он не
156 | * имеет доступа к состоянию создателя, хранящемуся внутри снимка. Он работает
157 | * со всеми снимками через базовый интерфейс Снимка.
158 | */
159 | class Caretaker {
160 | /**
161 | * @var Memento[]
162 | */
163 | private:
164 | std::vector mementos_;
165 |
166 | /**
167 | * @var Originator
168 | */
169 | Originator *originator_;
170 |
171 | public:
172 | Caretaker(Originator* originator) : originator_(originator) {
173 | }
174 |
175 | ~Caretaker() {
176 | for (auto m : mementos_) delete m;
177 | }
178 |
179 | void Backup() {
180 | std::cout << "\nCaretaker: Saving Originator's state...\n";
181 | this->mementos_.push_back(this->originator_->Save());
182 | }
183 | void Undo() {
184 | if (!this->mementos_.size()) {
185 | return;
186 | }
187 | Memento *memento = this->mementos_.back();
188 | this->mementos_.pop_back();
189 | std::cout << "Caretaker: Restoring state to: " << memento->GetName() << "\n";
190 | try {
191 | this->originator_->Restore(memento);
192 | } catch (...) {
193 | this->Undo();
194 | }
195 | }
196 | void ShowHistory() const {
197 | std::cout << "Caretaker: Here's the list of mementos:\n";
198 | for (Memento *memento : this->mementos_) {
199 | std::cout << memento->GetName() << "\n";
200 | }
201 | }
202 | };
203 | /**
204 | * EN: Client code.
205 | *
206 | * RU: Клиентский код.
207 | */
208 |
209 | void ClientCode() {
210 | Originator *originator = new Originator("Super-duper-super-puper-super.");
211 | Caretaker *caretaker = new Caretaker(originator);
212 | caretaker->Backup();
213 | originator->DoSomething();
214 | caretaker->Backup();
215 | originator->DoSomething();
216 | caretaker->Backup();
217 | originator->DoSomething();
218 | std::cout << "\n";
219 | caretaker->ShowHistory();
220 | std::cout << "\nClient: Now, let's rollback!\n\n";
221 | caretaker->Undo();
222 | std::cout << "\nClient: Once more!\n\n";
223 | caretaker->Undo();
224 |
225 | delete originator;
226 | delete caretaker;
227 | }
228 |
229 | int main() {
230 | std::srand(static_cast(std::time(NULL)));
231 | ClientCode();
232 | return 0;
233 | }
234 |
--------------------------------------------------------------------------------
/src/Observer/Conceptual/Output.txt:
--------------------------------------------------------------------------------
1 | Hi, I'm the Observer "1".
2 | Hi, I'm the Observer "2".
3 | Hi, I'm the Observer "3".
4 | There are 3 observers in the list.
5 | Observer "1": a new message is available --> Hello World! :D
6 | Observer "2": a new message is available --> Hello World! :D
7 | Observer "3": a new message is available --> Hello World! :D
8 | Observer "3" removed from the list.
9 | There are 2 observers in the list.
10 | Observer "1": a new message is available --> The weather is hot today! :p
11 | Observer "2": a new message is available --> The weather is hot today! :p
12 | Hi, I'm the Observer "4".
13 | Observer "2" removed from the list.
14 | Hi, I'm the Observer "5".
15 | There are 3 observers in the list.
16 | Observer "1": a new message is available --> My new car is great! ;)
17 | Observer "4": a new message is available --> My new car is great! ;)
18 | Observer "5": a new message is available --> My new car is great! ;)
19 | Observer "5" removed from the list.
20 | Observer "4" removed from the list.
21 | Observer "1" removed from the list.
22 | Goodbye, I was the Observer "5".
23 | Goodbye, I was the Observer "4".
24 | Goodbye, I was the Observer "3".
25 | Goodbye, I was the Observer "2".
26 | Goodbye, I was the Observer "1".
27 | Goodbye, I was the Subject.
--------------------------------------------------------------------------------
/src/Observer/Conceptual/main.cc:
--------------------------------------------------------------------------------
1 | /**
2 | * EN: Observer Design Pattern
3 | *
4 | * Intent: Lets you define a subscription mechanism to notify multiple objects
5 | * about any events that happen to the object they're observing.
6 | *
7 | * Note that there's a lot of different terms with similar meaning associated
8 | * with this pattern. Just remember that the Subject is also called the
9 | * Publisher and the Observer is often called the Subscriber and vice versa.
10 | * Also the verbs "observe", "listen" or "track" usually mean the same thing.
11 | *
12 | * RU: Паттерн Наблюдатель
13 | *
14 | * Назначение: Создаёт механизм подписки, позволяющий одним объектам следить и
15 | * реагировать на события, происходящие в других объектах.
16 | *
17 | * Обратите внимание, что существует множество различных терминов с похожими
18 | * значениями, связанных с этим паттерном. Просто помните, что Субъекта также
19 | * называют Издателем, а Наблюдателя часто называют Подписчиком и наоборот.
20 | * Также глаголы «наблюдать», «слушать» или «отслеживать» обычно означают одно и
21 | * то же.
22 | */
23 |
24 | #include
25 | #include
26 | #include
27 |
28 | class IObserver {
29 | public:
30 | virtual ~IObserver(){};
31 | virtual void Update(const std::string &message_from_subject) = 0;
32 | };
33 |
34 | class ISubject {
35 | public:
36 | virtual ~ISubject(){};
37 | virtual void Attach(IObserver *observer) = 0;
38 | virtual void Detach(IObserver *observer) = 0;
39 | virtual void Notify() = 0;
40 | };
41 |
42 | /**
43 | * EN: The Subject owns some important state and notifies observers when the
44 | * state changes.
45 | *
46 | * RU: Издатель владеет некоторым важным состоянием и оповещает наблюдателей о
47 | * его изменениях.
48 | */
49 |
50 | class Subject : public ISubject {
51 | public:
52 | virtual ~Subject() {
53 | std::cout << "Goodbye, I was the Subject.\n";
54 | }
55 |
56 | /**
57 | * EN: The subscription management methods.
58 | *
59 | * RU: Методы управления подпиской.
60 | */
61 | void Attach(IObserver *observer) override {
62 | list_observer_.push_back(observer);
63 | }
64 | void Detach(IObserver *observer) override {
65 | list_observer_.remove(observer);
66 | }
67 | void Notify() override {
68 | std::list::iterator iterator = list_observer_.begin();
69 | HowManyObserver();
70 | while (iterator != list_observer_.end()) {
71 | (*iterator)->Update(message_);
72 | ++iterator;
73 | }
74 | }
75 |
76 | void CreateMessage(std::string message = "Empty") {
77 | this->message_ = message;
78 | Notify();
79 | }
80 | void HowManyObserver() {
81 | std::cout << "There are " << list_observer_.size() << " observers in the list.\n";
82 | }
83 |
84 | /**
85 | * EN: Usually, the subscription logic is only a fraction of what a Subject
86 | * can really do. Subjects commonly hold some important business logic, that
87 | * triggers a notification method whenever something important is about to
88 | * happen (or after it).
89 | *
90 | * RU: Обычно логика подписки – только часть того, что делает Издатель.
91 | * Издатели часто содержат некоторую важную бизнес-логику, которая запускает
92 | * метод уведомления всякий раз, когда должно произойти что-то важное (или
93 | * после этого).
94 | */
95 | void SomeBusinessLogic() {
96 | this->message_ = "change message message";
97 | Notify();
98 | std::cout << "I'm about to do some thing important\n";
99 | }
100 |
101 | private:
102 | std::list list_observer_;
103 | std::string message_;
104 | };
105 |
106 | class Observer : public IObserver {
107 | public:
108 | Observer(Subject &subject) : subject_(subject) {
109 | this->subject_.Attach(this);
110 | std::cout << "Hi, I'm the Observer \"" << ++Observer::static_number_ << "\".\n";
111 | this->number_ = Observer::static_number_;
112 | }
113 | virtual ~Observer() {
114 | std::cout << "Goodbye, I was the Observer \"" << this->number_ << "\".\n";
115 | }
116 |
117 | void Update(const std::string &message_from_subject) override {
118 | message_from_subject_ = message_from_subject;
119 | PrintInfo();
120 | }
121 | void RemoveMeFromTheList() {
122 | subject_.Detach(this);
123 | std::cout << "Observer \"" << number_ << "\" removed from the list.\n";
124 | }
125 | void PrintInfo() {
126 | std::cout << "Observer \"" << this->number_ << "\": a new message is available --> " << this->message_from_subject_ << "\n";
127 | }
128 |
129 | private:
130 | std::string message_from_subject_;
131 | Subject &subject_;
132 | static int static_number_;
133 | int number_;
134 | };
135 |
136 | int Observer::static_number_ = 0;
137 |
138 | void ClientCode() {
139 | Subject *subject = new Subject;
140 | Observer *observer1 = new Observer(*subject);
141 | Observer *observer2 = new Observer(*subject);
142 | Observer *observer3 = new Observer(*subject);
143 | Observer *observer4;
144 | Observer *observer5;
145 |
146 | subject->CreateMessage("Hello World! :D");
147 | observer3->RemoveMeFromTheList();
148 |
149 | subject->CreateMessage("The weather is hot today! :p");
150 | observer4 = new Observer(*subject);
151 |
152 | observer2->RemoveMeFromTheList();
153 | observer5 = new Observer(*subject);
154 |
155 | subject->CreateMessage("My new car is great! ;)");
156 | observer5->RemoveMeFromTheList();
157 |
158 | observer4->RemoveMeFromTheList();
159 | observer1->RemoveMeFromTheList();
160 |
161 | delete observer5;
162 | delete observer4;
163 | delete observer3;
164 | delete observer2;
165 | delete observer1;
166 | delete subject;
167 | }
168 |
169 | int main() {
170 | ClientCode();
171 | return 0;
172 | }
173 |
--------------------------------------------------------------------------------
/src/Prototype/Conceptual/Output.txt:
--------------------------------------------------------------------------------
1 | Let's create a Prototype 1
2 | Call Method from PROTOTYPE_1 with field : 90
3 |
4 | Let's create a Prototype 2
5 | Call Method from PROTOTYPE_2 with field : 10
--------------------------------------------------------------------------------
/src/Prototype/Conceptual/main.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | using std::string;
6 |
7 | // EN: Prototype Design Pattern
8 | //
9 | // Intent: Lets you copy existing objects without making your code dependent on
10 | // their classes.
11 | //
12 | // RU: Паттерн Прототип
13 | //
14 | // Назначение: Позволяет копировать объекты, не вдаваясь в подробности их
15 | // реализации.
16 |
17 | enum Type {
18 | PROTOTYPE_1 = 0,
19 | PROTOTYPE_2
20 | };
21 |
22 | /**
23 | * EN: The example class that has cloning ability. We'll see how the values of
24 | * field with different types will be cloned.
25 | *
26 | * RU: Пример класса, имеющего возможность клонирования. Мы посмотрим как
27 | * происходит клонирование значений полей разных типов.
28 | */
29 |
30 | class Prototype {
31 | protected:
32 | string prototype_name_;
33 | float prototype_field_;
34 |
35 | public:
36 | Prototype() {}
37 | Prototype(string prototype_name)
38 | : prototype_name_(prototype_name) {
39 | }
40 | virtual ~Prototype() {}
41 | virtual Prototype *Clone() const = 0;
42 | virtual void Method(float prototype_field) {
43 | this->prototype_field_ = prototype_field;
44 | std::cout << "Call Method from " << prototype_name_ << " with field : " << prototype_field << std::endl;
45 | }
46 | };
47 |
48 | /**
49 | * EN: ConcretePrototype1 is a Sub-Class of Prototype and implement the Clone Method
50 | * In this example all data members of Prototype Class are in the Stack. If you
51 | * have pointers in your properties for ex: String* name_ ,you will need to
52 | * implement the Copy-Constructor to make sure you have a deep copy from the
53 | * clone method
54 | *
55 | * RU:
56 | */
57 |
58 | class ConcretePrototype1 : public Prototype {
59 | private:
60 | float concrete_prototype_field1_;
61 |
62 | public:
63 | ConcretePrototype1(string prototype_name, float concrete_prototype_field)
64 | : Prototype(prototype_name), concrete_prototype_field1_(concrete_prototype_field) {
65 | }
66 |
67 | /**
68 | * EN: Notice that Clone method return a Pointer to a new ConcretePrototype1 replica. so, the client
69 | * (who call the clone method) has the responsability to free that memory. If you have
70 | * smart pointer knowledge you may prefer to use unique_pointer here.
71 | *
72 | * RU:
73 | */
74 | Prototype *Clone() const override {
75 | return new ConcretePrototype1(*this);
76 | }
77 | };
78 |
79 | class ConcretePrototype2 : public Prototype {
80 | private:
81 | float concrete_prototype_field2_;
82 |
83 | public:
84 | ConcretePrototype2(string prototype_name, float concrete_prototype_field)
85 | : Prototype(prototype_name), concrete_prototype_field2_(concrete_prototype_field) {
86 | }
87 | Prototype *Clone() const override {
88 | return new ConcretePrototype2(*this);
89 | }
90 | };
91 |
92 | /**
93 | * EN: In PrototypeFactory you have two concrete prototypes, one for each concrete
94 | * prototype class, so each time you want to create a bullet ,
95 | * you can use the existing ones and clone those.
96 | *
97 | * RU:
98 | */
99 |
100 | class PrototypeFactory {
101 | private:
102 | std::unordered_map> prototypes_;
103 |
104 | public:
105 | PrototypeFactory() {
106 | prototypes_[Type::PROTOTYPE_1] = new ConcretePrototype1("PROTOTYPE_1 ", 50.f);
107 | prototypes_[Type::PROTOTYPE_2] = new ConcretePrototype2("PROTOTYPE_2 ", 60.f);
108 | }
109 |
110 | /**
111 | * EN: Be carefull of free all memory allocated. Again, if you have smart pointers knowelege
112 | * will be better to use it here.
113 | *
114 | * RU:
115 | */
116 |
117 | ~PrototypeFactory() {
118 | delete prototypes_[Type::PROTOTYPE_1];
119 | delete prototypes_[Type::PROTOTYPE_2];
120 | }
121 |
122 | /**
123 | * EN: Notice here that you just need to specify the type of the prototype you want and the method
124 | * will create from the object with this type.
125 | *
126 | * RU:
127 | */
128 | Prototype *CreatePrototype(Type type) {
129 | return prototypes_[type]->Clone();
130 | }
131 | };
132 |
133 | void Client(PrototypeFactory &prototype_factory) {
134 | std::cout << "Let's create a Prototype 1\n";
135 |
136 | Prototype *prototype = prototype_factory.CreatePrototype(Type::PROTOTYPE_1);
137 | prototype->Method(90);
138 | delete prototype;
139 |
140 | std::cout << "\n";
141 |
142 | std::cout << "Let's create a Prototype 2 \n";
143 |
144 | prototype = prototype_factory.CreatePrototype(Type::PROTOTYPE_2);
145 | prototype->Method(10);
146 |
147 | delete prototype;
148 | }
149 |
150 | int main() {
151 | PrototypeFactory *prototype_factory = new PrototypeFactory();
152 | Client(*prototype_factory);
153 | delete prototype_factory;
154 |
155 | return 0;
156 | }
--------------------------------------------------------------------------------
/src/Proxy/Conceptual/Output.txt:
--------------------------------------------------------------------------------
1 | Client: Executing the client code with a real subject:
2 | RealSubject: Handling request.
3 |
4 | Client: Executing the same client code with a proxy:
5 | Proxy: Checking access prior to firing a real request.
6 | RealSubject: Handling request.
7 | Proxy: Logging the time of request.
--------------------------------------------------------------------------------
/src/Proxy/Conceptual/main.cc:
--------------------------------------------------------------------------------
1 | #include
2 | /**
3 | * EN: Proxy Design Pattern
4 | *
5 | * Intent: Provide a surrogate or placeholder for another object to control
6 | * access to the original object or to add other responsibilities.
7 | *
8 | * RU: Паттерн Заместитель
9 | *
10 | * Назначение: Позволяет подставлять вместо реальных объектов специальные
11 | * объекты-заменители. Эти объекты перехватывают вызовы к оригинальному объекту,
12 | * позволяя сделать что-то до или после передачи вызова оригиналу.
13 | */
14 | /**
15 | * EN: The Subject interface declares common operations for both RealSubject and
16 | * the Proxy. As long as the client works with RealSubject using this interface,
17 | * you'll be able to pass it a proxy instead of a real subject.
18 | *
19 | * RU: Интерфейс Субъекта объявляет общие операции как для Реального Субъекта,
20 | * так и для Заместителя. Пока клиент работает с Реальным Субъектом, используя
21 | * этот интерфейс, вы сможете передать ему заместителя вместо реального
22 | * субъекта.
23 | */
24 | class Subject {
25 | public:
26 | virtual void Request() const = 0;
27 | };
28 | /**
29 | * EN: The RealSubject contains some core business logic. Usually, RealSubjects
30 | * are capable of doing some useful work which may also be very slow or
31 | * sensitive - e.g. correcting input data. A Proxy can solve these issues
32 | * without any changes to the RealSubject's code.
33 | *
34 | * RU: Реальный Субъект содержит некоторую базовую бизнес-логику. Как правило,
35 | * Реальные Субъекты способны выполнять некоторую полезную работу, которая к
36 | * тому же может быть очень медленной или точной – например, коррекция входных
37 | * данных. Заместитель может решить эти задачи без каких-либо изменений в коде
38 | * Реального Субъекта.
39 | */
40 | class RealSubject : public Subject {
41 | public:
42 | void Request() const override {
43 | std::cout << "RealSubject: Handling request.\n";
44 | }
45 | };
46 | /**
47 | * EN: The Proxy has an interface identical to the RealSubject.
48 | *
49 | * RU: Интерфейс Заместителя идентичен интерфейсу Реального Субъекта.
50 | */
51 | class Proxy : public Subject {
52 | /**
53 | * @var RealSubject
54 | */
55 | private:
56 | RealSubject *real_subject_;
57 |
58 | bool CheckAccess() const {
59 | // EN: Some real checks should go here.
60 | //
61 | // RU: Некоторые реальные проверки должны проходить здесь.
62 | std::cout << "Proxy: Checking access prior to firing a real request.\n";
63 | return true;
64 | }
65 | void LogAccess() const {
66 | std::cout << "Proxy: Logging the time of request.\n";
67 | }
68 |
69 | /**
70 | * EN: The Proxy maintains a reference to an object of the RealSubject
71 | * class. It can be either lazy-loaded or passed to the Proxy by the client.
72 | *
73 | * RU: Заместитель хранит ссылку на объект класса РеальныйСубъект. Клиент
74 | * может либо лениво загрузить его, либо передать Заместителю.
75 | */
76 | public:
77 | Proxy(RealSubject *real_subject) : real_subject_(new RealSubject(*real_subject)) {
78 | }
79 |
80 | ~Proxy() {
81 | delete real_subject_;
82 | }
83 | /**
84 | * EN: The most common applications of the Proxy pattern are lazy loading,
85 | * caching, controlling the access, logging, etc. A Proxy can perform one of
86 | * these things and then, depending on the result, pass the execution to the
87 | * same method in a linked RealSubject object.
88 | *
89 | * RU: Наиболее распространёнными областями применения паттерна Заместитель
90 | * являются ленивая загрузка, кэширование, контроль доступа, ведение журнала
91 | * и т.д. Заместитель может выполнить одну из этих задач, а затем, в
92 | * зависимости от результата, передать выполнение одноимённому методу в
93 | * связанном объекте класса Реального Субъект.
94 | */
95 | void Request() const override {
96 | if (this->CheckAccess()) {
97 | this->real_subject_->Request();
98 | this->LogAccess();
99 | }
100 | }
101 | };
102 | /**
103 | * EN: The client code is supposed to work with all objects (both subjects and
104 | * proxies) via the Subject interface in order to support both real subjects and
105 | * proxies. In real life, however, clients mostly work with their real subjects
106 | * directly. In this case, to implement the pattern more easily, you can extend
107 | * your proxy from the real subject's class.
108 | *
109 | * RU: Клиентский код должен работать со всеми объектами (как с реальными, так и
110 | * заместителями) через интерфейс Субъекта, чтобы поддерживать как реальные
111 | * субъекты, так и заместителей. В реальной жизни, однако, клиенты в основном
112 | * работают с реальными субъектами напрямую. В этом случае, для более простой
113 | * реализации паттерна, можно расширить заместителя из класса реального
114 | * субъекта.
115 | */
116 | void ClientCode(const Subject &subject) {
117 | // ...
118 | subject.Request();
119 | // ...
120 | }
121 |
122 | int main() {
123 | std::cout << "Client: Executing the client code with a real subject:\n";
124 | RealSubject *real_subject = new RealSubject;
125 | ClientCode(*real_subject);
126 | std::cout << "\n";
127 | std::cout << "Client: Executing the same client code with a proxy:\n";
128 | Proxy *proxy = new Proxy(real_subject);
129 | ClientCode(*proxy);
130 |
131 | delete real_subject;
132 | delete proxy;
133 | return 0;
134 | }
135 |
--------------------------------------------------------------------------------
/src/Singleton/Conceptual/NonThreadSafe/Output.txt:
--------------------------------------------------------------------------------
1 | If you see the same value, then singleton was reused (yay!
2 | If you see different values, then 2 singletons were created (booo!!)
3 |
4 | RESULT:
5 | BAR
6 | FOO
--------------------------------------------------------------------------------
/src/Singleton/Conceptual/NonThreadSafe/main.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | /**
6 | * EN: Singleton Design Pattern
7 | *
8 | * Intent: Lets you ensure that a class has only one instance, while providing a
9 | * global access point to this instance.
10 | *
11 | * RU: Паттерн Одиночка
12 | *
13 | * Назначение: Гарантирует, что у класса есть только один экземпляр, и
14 | * предоставляет к нему глобальную точку доступа.
15 | */
16 | /**
17 | * EN: The Singleton class defines the `GetInstance` method that serves as an
18 | * alternative to constructor and lets clients access the same instance of this
19 | * class over and over.
20 | *
21 | * RU: Класс Одиночка предоставляет метод `GetInstance`, который ведёт себя как
22 | * альтернативный конструктор и позволяет клиентам получать один и тот же
23 | * экземпляр класса при каждом вызове.
24 | */
25 | class Singleton
26 | {
27 |
28 | /**
29 | * EN: The Singleton's constructor should always be private to prevent
30 | * direct construction calls with the `new` operator.
31 | *
32 | * RU: Конструктор Одиночки всегда должен быть скрытым, чтобы предотвратить
33 | * создание объекта через оператор new.
34 | */
35 |
36 | protected:
37 | Singleton(const std::string value): value_(value)
38 | {
39 | }
40 |
41 | static Singleton* singleton_;
42 |
43 | std::string value_;
44 |
45 | public:
46 |
47 | /**
48 | * EN: Singletons should not be cloneable.
49 | *
50 | * RU: Одиночки не должны быть клонируемыми.
51 | */
52 | Singleton(Singleton &other) = delete;
53 | /**
54 | * EN: Singletons should not be assignable.
55 | *
56 | * RU:
57 | */
58 | void operator=(const Singleton &) = delete;
59 | /**
60 | * EN: This is the static method that controls the access to the singleton
61 | * instance. On the first run, it creates a singleton object and places it
62 | * into the static field. On subsequent runs, it returns the client existing
63 | * object stored in the static field.
64 | *
65 | *
66 | * RU: Это статический метод, управляющий доступом к экземпляру одиночки.
67 | * При первом запуске, он создаёт экземпляр одиночки и помещает его в
68 | * статическое поле. При последующих запусках, он возвращает клиенту объект,
69 | * хранящийся в статическом поле.
70 | *
71 | */
72 |
73 | static Singleton *GetInstance(const std::string& value);
74 | /**
75 | * EN: Finally, any singleton should define some business logic, which can
76 | * be executed on its instance.
77 | *
78 | * RU: Наконец, любой одиночка должен содержать некоторую бизнес-логику,
79 | * которая может быть выполнена на его экземпляре.
80 | */
81 | void SomeBusinessLogic()
82 | {
83 | // ...
84 | }
85 |
86 | std::string value() const{
87 | return value_;
88 | }
89 | };
90 |
91 | Singleton* Singleton::singleton_= nullptr;;
92 |
93 | /**
94 | * EN: Static methods should be defined outside the class.
95 | *
96 | * RU:
97 | */
98 | Singleton *Singleton::GetInstance(const std::string& value)
99 | {
100 | /**
101 | * EN: This is a safer way to create an instance. instance = new Singleton is dangeruous
102 | * in case two instance threads wants to access at the same time
103 | *
104 | * RU:
105 | */
106 | if(singleton_==nullptr){
107 | singleton_ = new Singleton(value);
108 | }
109 | return singleton_;
110 | }
111 |
112 | void ThreadFoo(){
113 | // EN: Following code emulates slow initialization.
114 | //
115 | // RU: Этот код эмулирует медленную инициализацию.
116 | std::this_thread::sleep_for(std::chrono::milliseconds(1000));
117 | Singleton* singleton = Singleton::GetInstance("FOO");
118 | std::cout << singleton->value() << "\n";
119 | }
120 |
121 | void ThreadBar(){
122 | // EN: Following code emulates slow initialization.
123 | //
124 | // RU: Этот код эмулирует медленную инициализацию.
125 | std::this_thread::sleep_for(std::chrono::milliseconds(1000));
126 | Singleton* singleton = Singleton::GetInstance("BAR");
127 | std::cout << singleton->value() << "\n";
128 | }
129 |
130 |
131 | int main()
132 | {
133 | std::cout <<"If you see the same value, then singleton was reused (yay!\n" <<
134 | "If you see different values, then 2 singletons were created (booo!!)\n\n" <<
135 | "RESULT:\n";
136 | std::thread t1(ThreadFoo);
137 | std::thread t2(ThreadBar);
138 | t1.join();
139 | t2.join();
140 |
141 | return 0;
142 | }
--------------------------------------------------------------------------------
/src/Singleton/Conceptual/ThreadSafe/Output.txt:
--------------------------------------------------------------------------------
1 | If you see the same value, then singleton was reused (yay!
2 | If you see different values, then 2 singletons were created (booo!!)
3 |
4 | RESULT:
5 | FOO
6 | FOO
--------------------------------------------------------------------------------
/src/Singleton/Conceptual/ThreadSafe/main.cc:
--------------------------------------------------------------------------------
1 | /**
2 | * EN: Have in mind it is an ilustrative trivial example, in real world
3 | * you may have in mind some more possible issues.
4 | *
5 | * RU:
6 | */
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | /**
13 | * EN: Singleton Design Pattern
14 | *
15 | * Intent: Lets you ensure that a class has only one instance, while providing a
16 | * global access point to this instance.
17 | *
18 | * RU: Паттерн Одиночка
19 | *
20 | * Назначение: Гарантирует, что у класса есть только один экземпляр, и
21 | * предоставляет к нему глобальную точку доступа.
22 | */
23 | /**
24 | * EN: The Singleton class defines the `GetInstance` method that serves as an
25 | * alternative to constructor and lets clients access the same instance of this
26 | * class over and over.
27 | *
28 | * RU: Класс Одиночка предоставляет метод `GetInstance`, который ведёт себя как
29 | * альтернативный конструктор и позволяет клиентам получать один и тот же
30 | * экземпляр класса при каждом вызове.
31 | */
32 | class Singleton
33 | {
34 |
35 | /**
36 | * EN: The Singleton's constructor/destructor should always be private to prevent
37 | * direct construction/desctruction calls with the `new`/`delete` operator.
38 | *
39 | * RU: Конструктор Одиночки всегда должен быть скрытым, чтобы предотвратить
40 | * создание объекта через оператор new.
41 | */
42 | private:
43 | static Singleton * pinstance_;
44 | static std::mutex mutex_;
45 |
46 | protected:
47 | Singleton(const std::string value): value_(value)
48 | {
49 | }
50 | ~Singleton() {}
51 | std::string value_;
52 |
53 | public:
54 | /**
55 | * EN: Singletons should not be cloneable.
56 | *
57 | * RU: Одиночки не должны быть клонируемыми.
58 | */
59 | Singleton(Singleton &other) = delete;
60 | /**
61 | * EN: Singletons should not be assignable.
62 | *
63 | * RU:
64 | */
65 | void operator=(const Singleton &) = delete;
66 | /**
67 | * EN: This is the static method that controls the access to the singleton
68 | * instance. On the first run, it creates a singleton object and places it
69 | * into the static field. On subsequent runs, it returns the client existing
70 | * object stored in the static field.
71 | *
72 | *
73 | * RU: Это статический метод, управляющий доступом к экземпляру одиночки.
74 | * При первом запуске, он создаёт экземпляр одиночки и помещает его в
75 | * статическое поле. При последующих запусках, он возвращает клиенту объект,
76 | * хранящийся в статическом поле.
77 | *
78 | */
79 |
80 | static Singleton *GetInstance(const std::string& value);
81 | /**
82 | * EN: Finally, any singleton should define some business logic, which can
83 | * be executed on its instance.
84 | *
85 | * RU: Наконец, любой одиночка должен содержать некоторую бизнес-логику,
86 | * которая может быть выполнена на его экземпляре.
87 | */
88 | void SomeBusinessLogic()
89 | {
90 | // ...
91 | }
92 |
93 | std::string value() const{
94 | return value_;
95 | }
96 | };
97 |
98 | /**
99 | * EN: Static methods should be defined outside the class.
100 | *
101 | * RU:
102 | */
103 |
104 | Singleton* Singleton::pinstance_{nullptr};
105 | std::mutex Singleton::mutex_;
106 |
107 | /**
108 | * EN: The first time we call GetInstance we will lock the storage location
109 | * and then we make sure again that the variable is null and then we
110 | * set the value.
111 | * RU:
112 | */
113 | Singleton *Singleton::GetInstance(const std::string& value)
114 | {
115 | std::lock_guard lock(mutex_);
116 | if (pinstance_ == nullptr)
117 | {
118 | pinstance_ = new Singleton(value);
119 | }
120 | return pinstance_;
121 | }
122 |
123 | void ThreadFoo(){
124 | // EN: Following code emulates slow initialization.
125 | //
126 | // RU: Этот код эмулирует медленную инициализацию.
127 | std::this_thread::sleep_for(std::chrono::milliseconds(1000));
128 | Singleton* singleton = Singleton::GetInstance("FOO");
129 | std::cout << singleton->value() << "\n";
130 | }
131 |
132 | void ThreadBar(){
133 | // EN: Following code emulates slow initialization.
134 | //
135 | // RU: Этот код эмулирует медленную инициализацию.
136 | std::this_thread::sleep_for(std::chrono::milliseconds(1000));
137 | Singleton* singleton = Singleton::GetInstance("BAR");
138 | std::cout << singleton->value() << "\n";
139 | }
140 |
141 | int main()
142 | {
143 | std::cout <<"If you see the same value, then singleton was reused (yay!\n" <<
144 | "If you see different values, then 2 singletons were created (booo!!)\n\n" <<
145 | "RESULT:\n";
146 | std::thread t1(ThreadFoo);
147 | std::thread t2(ThreadBar);
148 | t1.join();
149 | t2.join();
150 |
151 | return 0;
152 | }
--------------------------------------------------------------------------------
/src/Singleton/RealWorld/Output.txt:
--------------------------------------------------------------------------------
1 | //// Logger Singleton ////
2 | **** LOGGER START UP ****
3 | 1 [WARNING]
4 | Be careful with this potential issue.
5 | 2 [INFO]
6 | Here are some extra details.
7 | 3 [ERROR]
8 | A major problem has caused a fatal stoppage.
9 | **** LOGGER SHUT DOWN ****
--------------------------------------------------------------------------------
/src/Singleton/RealWorld/main.cc:
--------------------------------------------------------------------------------
1 | /**
2 | * EN: Real World Example of the Singleton Design Pattern
3 | *
4 | * Need: Consider a (large) program that must implement its own internal logging
5 | * functionality with a global logger object. Suppose that all log messages are
6 | * required to be printed in order even if the logger is called across multiple
7 | * concurrent threads or processes. Furthermore, the logger should have some
8 | * sort of flag to specify and ignore messages below a certain level.
9 | *
10 | * Solution: A thread-safe Logger class can be implemented using the Scott
11 | * Meyers' Singleton pattern. The Singleton pattern is the recommended solution
12 | * if indeed there must be a single global instance of the Logger class.
13 | * However, in modern practices, the addition of a new singleton to a codebase
14 | * could be regarded as a design flaw with the singleton itself being a design
15 | * anti-pattern. Nevertheless, the following presents a Logger Singleton as a
16 | * commonly appearing use case of the pattern in the C++ literature.
17 | */
18 |
19 | #include
20 | #include
21 | #include
22 | #include
23 |
24 | /**
25 | * EN: The Logger Singleton Class
26 | *
27 | * In this (zero handle objects) implementation of the Scott Meyers' Singleton,
28 | * the constructor and destructor are private methods and the move/copy
29 | * constructors and assignment operations are explicitly deleted. In essence,
30 | * the program itself cannot directly create an instance of the Logger class,
31 | * and instead the static instance() member function must be used to access it.
32 | *
33 | * The public API of this Logger has two main callbacks: (1) set the level of
34 | * the Logger; and (2) log a message at given level. For convenience, these two
35 | * client-facing methods wrap around the instance() member function in a
36 | * thread-safe fashion. An integral counter member is also included to
37 | * demonstrate that the message ordering is preserved.
38 | *
39 | * Note the final keyword specifier prevents inheritance, that is, it is not
40 | * possible to extend this Logger Singleton and override its class methods.
41 | */
42 |
43 | class Logger final {
44 | public:
45 | /**
46 | * EN: Various levels for the log messages can be labelled here; the choice of
47 | * the level member establishes a threshold below which log messages are
48 | * ignored.
49 | */
50 | enum class Level : unsigned {
51 | debug = 0,
52 | info = 1,
53 | warning = 2,
54 | error = 3,
55 | /* ... */
56 | };
57 |
58 | public:
59 | /**
60 | * EN: The Public API of this Logger
61 | *
62 | * Note that both of these methods must be implemented in a thread-safe
63 | * manner, hence the mutex as a static member.
64 | */
65 | static void level(Level);
66 | static void log(std::string const &, Level level = Level::debug);
67 |
68 | public:
69 | /**
70 | * EN: Prevention of Copy and Move Construction
71 | */
72 | Logger(Logger const &) = delete;
73 | Logger(Logger &&) = delete;
74 |
75 | /**
76 | * EN: Prevention of Copy and Move Assigment Operations
77 | */
78 | Logger &operator=(Logger const &) = delete;
79 | Logger &operator=(Logger &&) = delete;
80 |
81 | /**
82 | * EN: Public Instantiator Method
83 | *
84 | * In a typical Singleton, this static member function would enable access to
85 | * the Singleton. In this implementation of a Logger class, it is called
86 | * inside of the bodies of the public API methods.
87 | */
88 | static Logger &instance();
89 |
90 | private:
91 | /**
92 | * EN: Private Constructor and Destructor
93 | */
94 | Logger();
95 | ~Logger();
96 |
97 | private:
98 | static std::mutex mutex_;
99 | static std::ostream &os_;
100 | static std::size_t count_;
101 | static Level level_;
102 | };
103 |
104 | /**
105 | * EN: Static members of the Logger class need to be defined outside of the
106 | * class itself.
107 | */
108 | std::mutex Logger::mutex_;
109 | std::ostream &Logger::os_{std::cout};
110 | std::size_t Logger::count_{0};
111 | Logger::Level Logger::level_{Logger::Level::debug};
112 |
113 | /**
114 | * EN: Magic Static (c.f. Scott Meyers' Singleton)
115 | *
116 | * The instance() method creates a local static instance of the Logger class,
117 | * which is guaranteed thread-safe initialisation without manual thread
118 | * synchronisation. Note that this does not guarantee the thread safety of other
119 | * members; the RAII (Resource Acquistion Is Initialisation) principle should be
120 | * used to lock and unlock the mutex.
121 | *
122 | * Note that there will be a performance penalty each time this method is
123 | * called as there will be a check to see if the instance has already been
124 | * initialised.
125 | */
126 | Logger &Logger::instance() {
127 | static Logger instance;
128 | return instance;
129 | }
130 |
131 | /**
132 | * EN: Logger Level Modifier Method
133 | *
134 | * This thread-safe setter allows the client to alter the (global) level member
135 | * of the Logger.
136 | */
137 |
138 | void Logger::level(Logger::Level level) {
139 | std::lock_guard lock(mutex_);
140 | instance().level_ = level;
141 | }
142 |
143 | /**
144 | * EN: Enummeration-to-String Helper Function
145 | *
146 | * This implementation is naive but nonetheless useful for distinguishing the
147 | * different kinds of log messages.
148 | */
149 | std::string to_string(Logger::Level level) {
150 | switch (level) {
151 | case Logger::Level::debug:
152 | return "[DEBUG]";
153 | case Logger::Level::info:
154 | return "[INFO]";
155 | case Logger::Level::warning:
156 | return "[WARNING]";
157 | case Logger::Level::error:
158 | return "[ERROR]";
159 | /* ... */
160 | default:
161 | return "[LEVEL]";
162 | }
163 | };
164 |
165 | /**
166 | * EN: Thread-Safe Log Method
167 | *
168 | * If the message level is at or above the threshold level of the Logger
169 | * Singleton, then the counter is incremented and the message is printed.
170 | * Otherwise, the message is ignored and the counter remains as is.
171 | *
172 | * Note again the usage of RAII for mutex locking/unlocking should this method
173 | * be called in a thread.
174 | */
175 | void Logger::log(std::string const &message, Logger::Level level) {
176 | std::lock_guard lock(mutex_);
177 | if (static_cast(level) < static_cast(instance().level_))
178 | return;
179 | instance().os_ << ++instance().count_ << '\t' << to_string(level) << "\n\t"
180 | << message << '\n';
181 | }
182 |
183 | /**
184 | * EN: Constructor and Destructor
185 | *
186 | * The print statements indicate when these methods are called in the program.
187 | */
188 | Logger::Logger() { std::cout << "****\tLOGGER\tSTART UP\t****" << '\n'; }
189 | Logger::~Logger() { std::cout << "****\tLOGGER\tSHUT DOWN\t****" << std::endl; }
190 |
191 | /**
192 | * EN: Client Code: Logger Singleton Usage
193 | *
194 | * The desired Log Level is set which also instantiates the Logger class; the
195 | * log() methods can then be invoked e.g. via lambdas within different threads.
196 | */
197 | int main() {
198 |
199 | std::cout << "//// Logger Singleton ////\n";
200 |
201 | Logger::level(Logger::Level::info);
202 |
203 | std::thread t1(
204 | [] { Logger::log("This is just a simple development check."); });
205 | std::thread t2(
206 | [] { Logger::log("Here are some extra details.", Logger::Level::info); });
207 | std::thread t3([] {
208 | Logger::log("Be careful with this potential issue.",
209 | Logger::Level::warning);
210 | });
211 | std::thread t4([] {
212 | Logger::log("A major problem has caused a fatal stoppage.",
213 | Logger::Level::error);
214 | });
215 |
216 | t1.join();
217 | t2.join();
218 | t3.join();
219 | t4.join();
220 |
221 | return EXIT_SUCCESS;
222 | }
--------------------------------------------------------------------------------
/src/State/Conceptual/Output.txt:
--------------------------------------------------------------------------------
1 | Context: Transition to 14ConcreteStateA.
2 | ConcreteStateA handles request1.
3 | ConcreteStateA wants to change the state of the context.
4 | Context: Transition to 14ConcreteStateB.
5 | ConcreteStateB handles request2.
6 | ConcreteStateB wants to change the state of the context.
7 | Context: Transition to 14ConcreteStateA.
8 |
9 |
--------------------------------------------------------------------------------
/src/State/Conceptual/main.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | /**
4 | * EN: State Design Pattern
5 | *
6 | * Intent: Lets an object alter its behavior when its internal state changes. It
7 | * appears as if the object changed its class.
8 | *
9 | * RU: Паттерн Состояние
10 | *
11 | * Назначение: Позволяет объектам менять поведение в зависимости от своего
12 | * состояния. Извне создаётся впечатление, что изменился класс объекта.
13 | */
14 |
15 | /**
16 | * EN: The base State class declares methods that all Concrete State should
17 | * implement and also provides a backreference to the Context object, associated
18 | * with the State. This backreference can be used by States to transition the
19 | * Context to another State.
20 | *
21 | * RU: Базовый класс Состояния объявляет методы, которые должны реализовать все
22 | * Конкретные Состояния, а также предоставляет обратную ссылку на объект
23 | * Контекст, связанный с Состоянием. Эта обратная ссылка может использоваться
24 | * Состояниями для передачи Контекста другому Состоянию.
25 | */
26 |
27 | class Context;
28 |
29 | class State {
30 | /**
31 | * @var Context
32 | */
33 | protected:
34 | Context *context_;
35 |
36 | public:
37 | virtual ~State() {
38 | }
39 |
40 | void set_context(Context *context) {
41 | this->context_ = context;
42 | }
43 |
44 | virtual void Handle1() = 0;
45 | virtual void Handle2() = 0;
46 | };
47 |
48 | /**
49 | * EN: The Context defines the interface of interest to clients. It also
50 | * maintains a reference to an instance of a State subclass, which represents
51 | * the current state of the Context.
52 | *
53 | * RU: Контекст определяет интерфейс, представляющий интерес для клиентов. Он
54 | * также хранит ссылку на экземпляр подкласса Состояния, который отображает
55 | * текущее состояние Контекста.
56 | */
57 | class Context {
58 | /**
59 | * EN: @var State A reference to the current state of the Context.
60 | *
61 | * RU: @var State Ссылка на текущее состояние Контекста.
62 | */
63 | private:
64 | State *state_;
65 |
66 | public:
67 | Context(State *state) : state_(nullptr) {
68 | this->TransitionTo(state);
69 | }
70 | ~Context() {
71 | delete state_;
72 | }
73 | /**
74 | * EN: The Context allows changing the State object at runtime.
75 | *
76 | * RU: Контекст позволяет изменять объект Состояния во время выполнения.
77 | */
78 | void TransitionTo(State *state) {
79 | std::cout << "Context: Transition to " << typeid(*state).name() << ".\n";
80 | if (this->state_ != nullptr)
81 | delete this->state_;
82 | this->state_ = state;
83 | this->state_->set_context(this);
84 | }
85 | /**
86 | * EN: The Context delegates part of its behavior to the current State
87 | * object.
88 | *
89 | * RU: Контекст делегирует часть своего поведения текущему объекту
90 | * Состояния.
91 | */
92 | void Request1() {
93 | this->state_->Handle1();
94 | }
95 | void Request2() {
96 | this->state_->Handle2();
97 | }
98 | };
99 |
100 | /**
101 | * EN: Concrete States implement various behaviors, associated with a state of
102 | * the Context.
103 | *
104 | * RU: Конкретные Состояния реализуют различные модели поведения, связанные с
105 | * состоянием Контекста.
106 | */
107 |
108 | class ConcreteStateA : public State {
109 | public:
110 | void Handle1() override;
111 |
112 | void Handle2() override {
113 | std::cout << "ConcreteStateA handles request2.\n";
114 | }
115 | };
116 |
117 | class ConcreteStateB : public State {
118 | public:
119 | void Handle1() override {
120 | std::cout << "ConcreteStateB handles request1.\n";
121 | }
122 | void Handle2() override {
123 | std::cout << "ConcreteStateB handles request2.\n";
124 | std::cout << "ConcreteStateB wants to change the state of the context.\n";
125 | this->context_->TransitionTo(new ConcreteStateA);
126 | }
127 | };
128 |
129 | void ConcreteStateA::Handle1() {
130 | {
131 | std::cout << "ConcreteStateA handles request1.\n";
132 | std::cout << "ConcreteStateA wants to change the state of the context.\n";
133 |
134 | this->context_->TransitionTo(new ConcreteStateB);
135 | }
136 | }
137 |
138 | /**
139 | * EN: The client code.
140 | *
141 | * RU: Клиентский код.
142 | */
143 | void ClientCode() {
144 | Context *context = new Context(new ConcreteStateA);
145 | context->Request1();
146 | context->Request2();
147 | delete context;
148 | }
149 |
150 | int main() {
151 | ClientCode();
152 | return 0;
153 | }
154 |
--------------------------------------------------------------------------------
/src/Strategy/Conceptual/Output.txt:
--------------------------------------------------------------------------------
1 | Client: Strategy is set to normal sorting.
2 | Context: Sorting data using the strategy (not sure how it'll do it)
3 | abcde
4 |
5 | Client: Strategy is set to reverse sorting.
6 | Context: Sorting data using the strategy (not sure how it'll do it)
7 | edcba
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/Strategy/Conceptual/main.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | /**
7 | * EN: Strategy Design Pattern
8 | *
9 | * Intent: Lets you define a family of algorithms, put each of them into a
10 | * separate class, and make their objects interchangeable.
11 | *
12 | * RU: Паттерн Стратегия
13 | *
14 | * Назначение: Определяет семейство схожих алгоритмов и помещает каждый из них в
15 | * собственный класс, после чего алгоритмы можно взаимозаменять прямо во время
16 | * исполнения программы.
17 | */
18 |
19 | /**
20 | * EN: The Strategy interface declares operations common to all supported
21 | * versions of some algorithm.
22 | *
23 | * The Context uses this interface to call the algorithm defined by Concrete
24 | * Strategies.
25 | *
26 | * RU: Интерфейс Стратегии объявляет операции, общие для всех поддерживаемых
27 | * версий некоторого алгоритма.
28 | *
29 | * Контекст использует этот интерфейс для вызова алгоритма, определённого
30 | * Конкретными Стратегиями.
31 | */
32 | class Strategy
33 | {
34 | public:
35 | virtual ~Strategy() = default;
36 | virtual std::string doAlgorithm(std::string_view data) const = 0;
37 | };
38 |
39 | /**
40 | * EN: The Context defines the interface of interest to clients.
41 | *
42 | * RU: Контекст определяет интерфейс, представляющий интерес для клиентов.
43 | */
44 |
45 | class Context
46 | {
47 | /**
48 | * EN: @var Strategy The Context maintains a reference to one of the
49 | * Strategy objects. The Context does not know the concrete class of a
50 | * strategy. It should work with all strategies via the Strategy interface.
51 | *
52 | * RU: @var Strategy Контекст хранит ссылку на один из объектов Стратегии.
53 | * Контекст не знает конкретного класса стратегии. Он должен работать со
54 | * всеми стратегиями через интерфейс Стратегии.
55 | */
56 | private:
57 | std::unique_ptr strategy_;
58 | /**
59 | *
60 | * EN: Usually, the Context accepts a strategy through the constructor, but
61 | * also provides a setter to change it at runtime.
62 | *
63 | * RU: Обычно Контекст принимает стратегию через конструктор, а также
64 | * предоставляет сеттер для её изменения во время выполнения.
65 | */
66 | public:
67 | explicit Context(std::unique_ptr &&strategy = {}) : strategy_(std::move(strategy))
68 | {
69 | }
70 | /**
71 | * EN: Usually, the Context allows replacing a Strategy object at runtime.
72 | *
73 | * RU: Обычно Контекст позволяет заменить объект Стратегии во время
74 | * выполнения.
75 | */
76 | void set_strategy(std::unique_ptr &&strategy)
77 | {
78 | strategy_ = std::move(strategy);
79 | }
80 | /**
81 | * EN: The Context delegates some work to the Strategy object instead of
82 | * implementing +multiple versions of the algorithm on its own.
83 | *
84 | * RU: Вместо того, чтобы самостоятельно реализовывать множественные версии
85 | * алгоритма, Контекст делегирует некоторую работу объекту Стратегии.
86 | */
87 | void doSomeBusinessLogic() const
88 | {
89 | if (strategy_) {
90 | std::cout << "Context: Sorting data using the strategy (not sure how it'll do it)\n";
91 | std::string result = strategy_->doAlgorithm("aecbd");
92 | std::cout << result << "\n";
93 | } else {
94 | std::cout << "Context: Strategy isn't set\n";
95 | }
96 | }
97 | };
98 |
99 | /**
100 | * EN: Concrete Strategies implement the algorithm while following the base
101 | * Strategy interface. The interface makes them interchangeable in the Context.
102 | *
103 | * RU: Конкретные Стратегии реализуют алгоритм, следуя базовому интерфейсу
104 | * Стратегии. Этот интерфейс делает их взаимозаменяемыми в Контексте.
105 | */
106 | class ConcreteStrategyA : public Strategy
107 | {
108 | public:
109 | std::string doAlgorithm(std::string_view data) const override
110 | {
111 | std::string result(data);
112 | std::sort(std::begin(result), std::end(result));
113 |
114 | return result;
115 | }
116 | };
117 | class ConcreteStrategyB : public Strategy
118 | {
119 | std::string doAlgorithm(std::string_view data) const override
120 | {
121 | std::string result(data);
122 | std::sort(std::begin(result), std::end(result), std::greater<>());
123 |
124 | return result;
125 | }
126 | };
127 | /**
128 | * EN: The client code picks a concrete strategy and passes it to the context.
129 | * The client should be aware of the differences between strategies in order to
130 | * make the right choice.
131 | *
132 | * RU: Клиентский код выбирает конкретную стратегию и передаёт её в контекст.
133 | * Клиент должен знать о различиях между стратегиями, чтобы сделать правильный
134 | * выбор.
135 | */
136 |
137 | void clientCode()
138 | {
139 | Context context(std::make_unique());
140 | std::cout << "Client: Strategy is set to normal sorting.\n";
141 | context.doSomeBusinessLogic();
142 | std::cout << "\n";
143 | std::cout << "Client: Strategy is set to reverse sorting.\n";
144 | context.set_strategy(std::make_unique());
145 | context.doSomeBusinessLogic();
146 | }
147 |
148 | int main()
149 | {
150 | clientCode();
151 | return 0;
152 | }
--------------------------------------------------------------------------------
/src/TemplateMethod/Conceptual/Output.txt:
--------------------------------------------------------------------------------
1 | Same client code can work with different subclasses:
2 | AbstractClass says: I am doing the bulk of the work
3 | ConcreteClass1 says: Implemented Operation1
4 | AbstractClass says: But I let subclasses override some operations
5 | ConcreteClass1 says: Implemented Operation2
6 | AbstractClass says: But I am doing the bulk of the work anyway
7 |
8 | Same client code can work with different subclasses:
9 | AbstractClass says: I am doing the bulk of the work
10 | ConcreteClass2 says: Implemented Operation1
11 | AbstractClass says: But I let subclasses override some operations
12 | ConcreteClass2 says: Overridden Hook1
13 | ConcreteClass2 says: Implemented Operation2
14 | AbstractClass says: But I am doing the bulk of the work anyway
15 |
16 |
--------------------------------------------------------------------------------
/src/TemplateMethod/Conceptual/main.cc:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | /**
4 | * EN: Template Method Design Pattern
5 | *
6 | * Intent: Defines the skeleton of an algorithm in the superclass but lets
7 | * subclasses override specific steps of the algorithm without changing its
8 | * structure.
9 | *
10 | * RU: Паттерн Шаблонный метод
11 | *
12 | * Назначение: Определяет общую схему алгоритма, перекладывая реализацию
13 | * некоторых шагов на подклассы. Шаблонный метод позволяет подклассам
14 | * переопределять отдельные шаги алгоритма без изменения структуры алгоритма.
15 | */
16 | /**
17 | * EN: The Abstract Class defines a template method that contains a skeleton of
18 | * some algorithm, composed of calls to (usually) abstract primitive operations.
19 | *
20 | * Concrete subclasses should implement these operations, but leave the template
21 | * method itself intact.
22 | *
23 | * RU: Абстрактный Класс определяет шаблонный метод, содержащий скелет
24 | * некоторого алгоритма, состоящего из вызовов (обычно) абстрактных примитивных
25 | * операций.
26 | *
27 | * Конкретные подклассы должны реализовать эти операции, но оставить сам
28 | * шаблонный метод без изменений.
29 | */
30 | class AbstractClass {
31 | /**
32 | * EN: The template method defines the skeleton of an algorithm.
33 | *
34 | * RU: Шаблонный метод определяет скелет алгоритма.
35 | */
36 | public:
37 | void TemplateMethod() const {
38 | this->BaseOperation1();
39 | this->RequiredOperations1();
40 | this->BaseOperation2();
41 | this->Hook1();
42 | this->RequiredOperation2();
43 | this->BaseOperation3();
44 | this->Hook2();
45 | }
46 | /**
47 | * EN: These operations already have implementations.
48 | *
49 | * RU: Эти операции уже имеют реализации.
50 | */
51 | protected:
52 | void BaseOperation1() const {
53 | std::cout << "AbstractClass says: I am doing the bulk of the work\n";
54 | }
55 | void BaseOperation2() const {
56 | std::cout << "AbstractClass says: But I let subclasses override some operations\n";
57 | }
58 | void BaseOperation3() const {
59 | std::cout << "AbstractClass says: But I am doing the bulk of the work anyway\n";
60 | }
61 | /**
62 | * EN: These operations have to be implemented in subclasses.
63 | *
64 | * RU: А эти операции должны быть реализованы в подклассах.
65 | */
66 | virtual void RequiredOperations1() const = 0;
67 | virtual void RequiredOperation2() const = 0;
68 | /**
69 | * EN: These are "hooks." Subclasses may override them, but it's not
70 | * mandatory since the hooks already have default (but empty)
71 | * implementation. Hooks provide additional extension points in some crucial
72 | * places of the algorithm.
73 | *
74 | * RU: Это «хуки». Подклассы могут переопределять их, но это не обязательно,
75 | * поскольку у хуков уже есть стандартная (но пустая) реализация. Хуки
76 | * предоставляют дополнительные точки расширения в некоторых критических
77 | * местах алгоритма.
78 | */
79 | virtual void Hook1() const {}
80 | virtual void Hook2() const {}
81 | };
82 | /**
83 | * EN: Concrete classes have to implement all abstract operations of the base
84 | * class. They can also override some operations with a default implementation.
85 | *
86 | * RU: Конкретные классы должны реализовать все абстрактные операции базового
87 | * класса. Они также могут переопределить некоторые операции с реализацией по
88 | * умолчанию.
89 | */
90 | class ConcreteClass1 : public AbstractClass {
91 | protected:
92 | void RequiredOperations1() const override {
93 | std::cout << "ConcreteClass1 says: Implemented Operation1\n";
94 | }
95 | void RequiredOperation2() const override {
96 | std::cout << "ConcreteClass1 says: Implemented Operation2\n";
97 | }
98 | };
99 | /**
100 | * EN: Usually, concrete classes override only a fraction of base class'
101 | * operations.
102 | *
103 | * RU: Обычно конкретные классы переопределяют только часть операций базового
104 | * класса.
105 | */
106 | class ConcreteClass2 : public AbstractClass {
107 | protected:
108 | void RequiredOperations1() const override {
109 | std::cout << "ConcreteClass2 says: Implemented Operation1\n";
110 | }
111 | void RequiredOperation2() const override {
112 | std::cout << "ConcreteClass2 says: Implemented Operation2\n";
113 | }
114 | void Hook1() const override {
115 | std::cout << "ConcreteClass2 says: Overridden Hook1\n";
116 | }
117 | };
118 | /**
119 | * EN: The client code calls the template method to execute the algorithm.
120 | * Client code does not have to know the concrete class of an object it works
121 | * with, as long as it works with objects through the interface of their base
122 | * class.
123 | *
124 | * RU: Клиентский код вызывает шаблонный метод для выполнения алгоритма.
125 | * Клиентский код не должен знать конкретный класс объекта, с которым работает,
126 | * при условии, что он работает с объектами через интерфейс их базового класса.
127 | */
128 | void ClientCode(AbstractClass *class_) {
129 | // ...
130 | class_->TemplateMethod();
131 | // ...
132 | }
133 |
134 | int main() {
135 | std::cout << "Same client code can work with different subclasses:\n";
136 | ConcreteClass1 *concreteClass1 = new ConcreteClass1;
137 | ClientCode(concreteClass1);
138 | std::cout << "\n";
139 | std::cout << "Same client code can work with different subclasses:\n";
140 | ConcreteClass2 *concreteClass2 = new ConcreteClass2;
141 | ClientCode(concreteClass2);
142 | delete concreteClass1;
143 | delete concreteClass2;
144 | return 0;
145 | }
146 |
--------------------------------------------------------------------------------
/src/Visitor/Conceptual/Output.txt:
--------------------------------------------------------------------------------
1 | The client code works with all visitors via the base Visitor interface:
2 | A + ConcreteVisitor1
3 | B + ConcreteVisitor1
4 |
5 | It allows the same client code to work with different types of visitors:
6 | A + ConcreteVisitor2
7 | B + ConcreteVisitor2
8 |
9 |
--------------------------------------------------------------------------------
/src/Visitor/Conceptual/main.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | /**
6 | * EN: Visitor Design Pattern
7 | *
8 | * Intent: Lets you separate algorithms from the objects on which they operate.
9 | *
10 | * RU: Паттерн Посетитель
11 | *
12 | * Назначение: Позволяет создавать новые операции, не меняя классы объектов, над
13 | * которыми эти операции могут выполняться.
14 | */
15 |
16 | /**
17 | * EN: The Visitor Interface declares a set of visiting methods that correspond
18 | * to component classes. The signature of a visiting method allows the visitor
19 | * to identify the exact class of the component that it's dealing with.
20 | *
21 | * RU: Интерфейс Посетителя объявляет набор методов посещения, соответствующих
22 | * классам компонентов. Сигнатура метода посещения позволяет посетителю
23 | * определить конкретный класс компонента, с которым он имеет дело.
24 | */
25 | class ConcreteComponentA;
26 | class ConcreteComponentB;
27 |
28 | class Visitor {
29 | public:
30 | virtual void VisitConcreteComponentA(const ConcreteComponentA *element) const = 0;
31 | virtual void VisitConcreteComponentB(const ConcreteComponentB *element) const = 0;
32 | };
33 |
34 | /**
35 | * EN: The Component interface declares an `accept` method that should take the
36 | * base visitor interface as an argument.
37 | *
38 | * RU: Интерфейс Компонента объявляет метод accept, который в качестве аргумента
39 | * может получать любой объект, реализующий интерфейс посетителя.
40 | */
41 |
42 | class Component {
43 | public:
44 | virtual ~Component() {}
45 | virtual void Accept(Visitor *visitor) const = 0;
46 | };
47 |
48 | /**
49 | * EN: Each Concrete Component must implement the `Accept` method in such a way
50 | * that it calls the visitor's method corresponding to the component's class.
51 | *
52 | * RU: Каждый Конкретный Компонент должен реализовать метод accept таким
53 | * образом, чтобы он вызывал метод посетителя, соответствующий классу
54 | * компонента.
55 | */
56 | class ConcreteComponentA : public Component {
57 | /**
58 | * EN: Note that we're calling `visitConcreteComponentA`, which matches the
59 | * current class name. This way we let the visitor know the class of the
60 | * component it works with.
61 | *
62 | * RU: Обратите внимание, мы вызываем visitConcreteComponentA, что
63 | * соответствует названию текущего класса. Таким образом мы позволяем
64 | * посетителю узнать, с каким классом компонента он работает.
65 | */
66 | public:
67 | void Accept(Visitor *visitor) const override {
68 | visitor->VisitConcreteComponentA(this);
69 | }
70 | /**
71 | * EN: Concrete Components may have special methods that don't exist in
72 | * their base class or interface. The Visitor is still able to use these
73 | * methods since it's aware of the component's concrete class.
74 | *
75 | * RU: Конкретные Компоненты могут иметь особые методы, не объявленные в их
76 | * базовом классе или интерфейсе. Посетитель всё же может использовать эти
77 | * методы, поскольку он знает о конкретном классе компонента.
78 | */
79 | std::string ExclusiveMethodOfConcreteComponentA() const {
80 | return "A";
81 | }
82 | };
83 |
84 | class ConcreteComponentB : public Component {
85 | /**
86 | * EN: Same here: visitConcreteComponentB => ConcreteComponentB
87 | *
88 | * RU: То же самое здесь: visitConcreteComponentB => ConcreteComponentB
89 | */
90 | public:
91 | void Accept(Visitor *visitor) const override {
92 | visitor->VisitConcreteComponentB(this);
93 | }
94 | std::string SpecialMethodOfConcreteComponentB() const {
95 | return "B";
96 | }
97 | };
98 |
99 | /**
100 | * EN: Concrete Visitors implement several versions of the same algorithm, which
101 | * can work with all concrete component classes.
102 | *
103 | * You can experience the biggest benefit of the Visitor pattern when using it
104 | * with a complex object structure, such as a Composite tree. In this case, it
105 | * might be helpful to store some intermediate state of the algorithm while
106 | * executing visitor's methods over various objects of the structure.
107 | *
108 | * RU: Конкретные Посетители реализуют несколько версий одного и того же
109 | * алгоритма, которые могут работать со всеми классами конкретных компонентов.
110 | *
111 | * Максимальную выгоду от паттерна Посетитель вы почувствуете, используя его со
112 | * сложной структурой объектов, такой как дерево Компоновщика. В этом случае
113 | * было бы полезно хранить некоторое промежуточное состояние алгоритма при
114 | * выполнении методов посетителя над различными объектами структуры.
115 | */
116 | class ConcreteVisitor1 : public Visitor {
117 | public:
118 | void VisitConcreteComponentA(const ConcreteComponentA *element) const override {
119 | std::cout << element->ExclusiveMethodOfConcreteComponentA() << " + ConcreteVisitor1\n";
120 | }
121 |
122 | void VisitConcreteComponentB(const ConcreteComponentB *element) const override {
123 | std::cout << element->SpecialMethodOfConcreteComponentB() << " + ConcreteVisitor1\n";
124 | }
125 | };
126 |
127 | class ConcreteVisitor2 : public Visitor {
128 | public:
129 | void VisitConcreteComponentA(const ConcreteComponentA *element) const override {
130 | std::cout << element->ExclusiveMethodOfConcreteComponentA() << " + ConcreteVisitor2\n";
131 | }
132 | void VisitConcreteComponentB(const ConcreteComponentB *element) const override {
133 | std::cout << element->SpecialMethodOfConcreteComponentB() << " + ConcreteVisitor2\n";
134 | }
135 | };
136 | /**
137 | * EN: The client code can run visitor operations over any set of elements
138 | * without figuring out their concrete classes. The accept operation directs a
139 | * call to the appropriate operation in the visitor object.
140 | *
141 | * RU: Клиентский код может выполнять операции посетителя над любым набором
142 | * элементов, не выясняя их конкретных классов. Операция принятия направляет
143 | * вызов к соответствующей операции в объекте посетителя.
144 | */
145 | void ClientCode(std::array components, Visitor *visitor) {
146 | // ...
147 | for (const Component *comp : components) {
148 | comp->Accept(visitor);
149 | }
150 | // ...
151 | }
152 |
153 | int main() {
154 | std::array components = {new ConcreteComponentA, new ConcreteComponentB};
155 | std::cout << "The client code works with all visitors via the base Visitor interface:\n";
156 | ConcreteVisitor1 *visitor1 = new ConcreteVisitor1;
157 | ClientCode(components, visitor1);
158 | std::cout << "\n";
159 | std::cout << "It allows the same client code to work with different types of visitors:\n";
160 | ConcreteVisitor2 *visitor2 = new ConcreteVisitor2;
161 | ClientCode(components, visitor2);
162 |
163 | for (const Component *comp : components) {
164 | delete comp;
165 | }
166 | delete visitor1;
167 | delete visitor2;
168 |
169 | return 0;
170 | }
171 |
--------------------------------------------------------------------------------
/src/Visitor/RealWorld/Output.txt:
--------------------------------------------------------------------------------
1 | {"menu":[{"item":"food","name":"Borscht","calories":"160kcal","label":"meat"},{"item":"food","name":"Samosa","calories":"250kcal","label":"vegetarian"},{"item":"food","name":"Sushi","calories":"300kcal","label":"fish"},{"item":"food","name":"Quinoa","calories":"350kcal","label":"vegan"},{"item":"drink","name":"Vodka","volume":"25ml","label":"alcholic"},{"item":"drink","name":"Chai","volume":"120ml","label":"hot"},{"item":"drink","name":"Sake","volume":"180ml","label":"alcholic"},{"item":"drink","name":"Kola","volume":"355ml","label":"cold"}]}
--------------------------------------------------------------------------------
/src/Visitor/RealWorld/main.cc:
--------------------------------------------------------------------------------
1 | /**
2 | * EN: Real World Example of the Visitor Design Pattern (Modern C++17 Standard)
3 | *
4 | * Need: Consider a restaurant \c Menu represented as a heterogeneous \c Item
5 | * collection of different \c Food and \c Drink items, which must be
6 | * (homogeneously) serialised into RFC 8259 JSON for some external API usage.
7 | *
8 | * Solution: A modern C++17 standard \c Serialiser Visitor be easily implemented
9 | * using the built-in utilities found within the \c header, namely,
10 | * the type-safe union \c std::variant to represent different menu items and the
11 | * functor \c std::visit to apply a callable \c Serialiser visitor.
12 | *
13 | * This simpler ("KISS") and boilerplate-free implementation of the Visitor
14 | * Design Pattern surpasses the classical object-oriented programming Visitor
15 | * that often requires maintaining two separate, but cyclically interdependent,
16 | * class hierarchies:
17 | *
18 | * \c Item<-Food/Drink and \c Visitor<-FoodVisitor/DrinkVisitor
19 | *
20 | * and suffers from performance penalties associated with the virtual function
21 | * calls in the double dispatch.
22 | *
23 | * In this contemporary take on the Visitor Design Pattern here, the (SOLID)
24 | * Open-Closed Principle is more expressively fulfilled because the \c Food and
25 | * \c Drink classes do not need to be derived from some base \c Item class and
26 | * also do not need to be updated with \c AcceptVisitor methods. The absence of
27 | * any intrusive polymorphism provides greater flexibility; this means that new
28 | * types (i.e. new \c Item types such as \c Snack ) and new visitors (i.e. ) are
29 | * more straightforward to incorporate.
30 | *
31 | * For such a \e procedural Visitor Design Pattern, performances gains can be
32 | * expected if the \c std::variant uses value semantics rather than reference
33 | * semantics, and if the collection storage is continguous, that is, instead of
34 | * the memory-scattering pointer indirections of the traditional Visitor Design
35 | * Pattern applied to multiple types.
36 | */
37 |
38 | #include
39 | #include
40 | #include
41 | #include
42 |
43 | /**
44 | * EN: Stable Low-Lying Data Structures for Food, Drink,...
45 | *
46 | * Respecting the Open-Closed Principle, there is no need to modify these
47 | * classes to accept the visitors that are to be introduced later. Observe that
48 | * these \c Item classes are not part of an inheritance hierarchy and so there
49 | * is flexibility to create more such \c Item classes.
50 | *
51 | * However, note that these classes require a complete definition here in lieu
52 | * of their upcoming role within the \c std::variant union, that is, a forward
53 | * declaration of these classes is not sufficient. The public API consists of an
54 | * explicit constructor and the necessary access methods that are required by
55 | * the \c Serialiser Visitor.
56 | */
57 | class Food {
58 | public:
59 | enum Label : unsigned { meat, fish, vegetarian, vegan };
60 |
61 | public:
62 | explicit Food(std::string name, std::size_t calories, Label label)
63 | : name_{name}, calories_{calories}, label_{label} {}
64 |
65 | auto name() const noexcept { return name_; }
66 | auto calories() const noexcept { return calories_; }
67 | auto label() const noexcept {
68 | switch (label_) {
69 | case Label::meat:
70 | return "meat";
71 | case Label::fish:
72 | return "fish";
73 | case Label::vegetarian:
74 | return "vegetarian";
75 | case Label::vegan:
76 | return "vegan";
77 | default:
78 | return "unknown";
79 | }
80 | }
81 |
82 | private:
83 | std::string name_;
84 | std::size_t calories_;
85 | Label label_;
86 | };
87 |
88 | class Drink {
89 | public:
90 | enum Label : unsigned { alcoholic, hot, cold };
91 |
92 | public:
93 | explicit Drink(std::string name, std::size_t volume, Label label)
94 | : name_{name}, volume_{volume}, label_{label} {}
95 |
96 | auto name() const noexcept { return name_; }
97 | auto volume() const noexcept { return volume_; }
98 | auto label() const noexcept {
99 | switch (label_) {
100 | case Label::alcoholic:
101 | return "alcholic";
102 | case Label::hot:
103 | return "hot";
104 | case Label::cold:
105 | return "cold";
106 | default:
107 | return "unknown";
108 | }
109 | }
110 |
111 | private:
112 | std::string name_;
113 | std::size_t volume_;
114 | Label label_;
115 | };
116 |
117 | /* ... */
118 |
119 | /**
120 | * EN: Variant Union of the Item and Menu as an Item Collection
121 | *
122 | * The \c Item and \c Menu aliases carve out an architectural boundary
123 | * separating the low-lying data structures (above) and the client-facing
124 | * visitor (below), the former being more established in the codebase and the
125 | * latter being perhaps newer and often more changeable. Also note the value
126 | * semantics, which means there is no need for manual dynamic memory allocation
127 | * or management (e.g. via smart pointers) and hence lower overall complexity
128 | * when it comes to implementing the Visitor Design Pattern.
129 | *
130 | * For best performance, it is recommended to use \c Item types of similar, if
131 | * not identical, sizes so that the memory layout can be optimised. (If there
132 | * are considerable differences in the class sizes, then it may be sensible to
133 | * use the Proxy Design Pattern to wrap around larger-sized classes or even the
134 | * Bridge Design Pattern/"pimpl" idiom.) The memory layout of the members within
135 | * each of the \c Item classes themselves may also be of importance to overall
136 | * performance (c.f. padding) in this implementation as the \c std::visit method
137 | * will be applied to each \c Item element by iterating over the \c Menu
138 | * container.
139 | */
140 | using Item = std::variant;
141 | using Menu = std::vector- ;
142 |
143 | /**
144 | * EN: Serialiser Visitor Functor
145 | *
146 | * This basic \c Serialiser class has non-canonical operator() overloads
147 | * which take the different \c Item types as input arguments, define lambdas to
148 | * perform a rudimentary conversion of the data to compressed/minified JSON
149 | * using the public API of the classes, and then print out the converted result
150 | * to some \c std::ostream by invoking the lambdas. Each \c Item has its own
151 | * unique overloaded operator() definition, which makes this class a prime
152 | * candidate for the Strategy Design Pattern e.g. different JSON specifications.
153 | */
154 | class Serialiser {
155 | public:
156 | explicit Serialiser(std::ostream &os = std::cout) : os_{os} {}
157 |
158 | public:
159 | auto operator()(Food const &food) const {
160 |
161 | auto to_json = [&](auto f) {
162 | return R"({"item":"food","name":")" + f.name() + R"(","calories":")" +
163 | std::to_string(f.calories()) + R"(kcal","label":")" + f.label() +
164 | R"("})";
165 | };
166 |
167 | os_ << to_json(food);
168 | }
169 | auto operator()(Drink const &drink) const {
170 | auto to_json = [&](auto d) {
171 | return R"({"item":"drink","name":")" + d.name() + R"(","volume":")" +
172 | std::to_string(d.volume()) + R"(ml","label":")" + d.label() +
173 | R"("})";
174 | };
175 | os_ << to_json(drink);
176 | }
177 | /* ... */
178 |
179 | private:
180 | std::ostream &os_{std::cout};
181 | };
182 |
183 | /* ... */
184 |
185 | /**
186 | * EN: Applied Visitor for Menu (Item Collection) Serialisation
187 | *
188 | * The callable/invokable \c Serialiser Visitor can now be applied to each of
189 | * the \c Item elements in the \c Menu via the \c std::visit utility method, the
190 | * internal machinery of which could somewhat vary between different compilers
191 | * (e.g. GCC, Clang, MSVC, etc.) and their versions. Nevertheless, as a staple
192 | * part of the standard library from C++17 onwards, \c std::visit reliably and
193 | * conveniently automates the required boilerplate code and thereby reduces the
194 | * implementational friction that accompanies the traditional object-oriented
195 | * Visitor Design Pattern.
196 | *
197 | * Accordingly, it is now possible to perform a simple range-based for loop over
198 | * the \c Menu collection and apply visitor on each \c Item element in turn,
199 | * which has the best possible performance if the \c Item elements are stored
200 | * contiguously as values in memory.
201 | */
202 | void serialise(Menu const &menu, std::ostream &os = std::cout) {
203 | bool first{true};
204 | os << R"({"menu":[)";
205 | for (auto const &item : menu) {
206 | if (!first)
207 | os << ",";
208 | else
209 | first = false;
210 | std::visit(Serialiser{os}, item);
211 | }
212 | os << R"(]})";
213 | }
214 |
215 | /* ... */
216 |
217 | /**
218 | * EN: Client Code: Variant Visitor
219 | *
220 | * The declaration of the \c Menu collection is clean and hassle-free, and the
221 | * addition of the \c Item elements in form of \c Food and \c Drink class
222 | * instances is also drastically simplified by the value semantics. Finally, the
223 | * neat \c serialise method can be called with the \c Menu input argument to
224 | * demonstrate Modern C++17 Visitor Design Pattern in action.
225 | */
226 | int main() {
227 |
228 | Menu menu;
229 | menu.reserve(8);
230 |
231 | menu.emplace_back(Food{"Borscht", 160, Food::Label::meat});
232 | menu.emplace_back(Food{"Samosa", 250, Food::Label::vegetarian});
233 | menu.emplace_back(Food{"Sushi", 300, Food::Label::fish});
234 | menu.emplace_back(Food{"Quinoa", 350, Food::Label::vegan});
235 | menu.emplace_back(Drink{"Vodka", 25, Drink::Label::alcoholic});
236 | menu.emplace_back(Drink{"Chai", 120, Drink::Label::hot});
237 | menu.emplace_back(Drink{"Sake", 180, Drink::Label::alcoholic});
238 | menu.emplace_back(Drink{"Kola", 355, Drink::Label::cold});
239 | /* ... */
240 |
241 | serialise(menu);
242 | }
--------------------------------------------------------------------------------