├── .gitignore ├── .vscode ├── c_cpp_properties.json ├── launch.json └── tasks.json ├── LICENSE.txt ├── README.md ├── bin └── .gitignore └── src ├── AbstractFactory └── Conceptual │ ├── Output.txt │ └── main.cc ├── Adapter └── Conceptual │ ├── MultipleInheritance │ ├── Output.txt │ └── main.cc │ └── Normal │ ├── Output.txt │ └── main.cc ├── Bridge └── Conceptual │ ├── Output.txt │ └── main.cc ├── Builder ├── Conceptual │ ├── Output.txt │ └── main.cc └── RealWorld │ ├── Output.txt │ └── main.cc ├── ChainOfResponsibility └── Conceptual │ ├── Output.txt │ └── main.cc ├── Command └── Conceptual │ ├── Output.txt │ └── main.cc ├── Composite └── Conceptual │ ├── Output.txt │ └── main.cc ├── Decorator └── Conceptual │ ├── Output.txt │ └── main.cc ├── Facade └── Conceptual │ ├── Output.txt │ └── main.cc ├── FactoryMethod └── Conceptual │ ├── Output.txt │ └── main.cc ├── Flyweight └── Conceptual │ ├── Output.txt │ └── main.cc ├── Iterator └── Conceptual │ ├── Output.txt │ └── main.cc ├── Mediator └── Conceptual │ ├── Output.txt │ └── main.cc ├── Memento └── Conceptual │ ├── Output.txt │ └── main.cc ├── Observer └── Conceptual │ ├── Output.txt │ └── main.cc ├── Prototype └── Conceptual │ ├── Output.txt │ └── main.cc ├── Proxy └── Conceptual │ ├── Output.txt │ └── main.cc ├── Singleton ├── Conceptual │ ├── NonThreadSafe │ │ ├── Output.txt │ │ └── main.cc │ └── ThreadSafe │ │ ├── Output.txt │ │ └── main.cc └── RealWorld │ ├── Output.txt │ └── main.cc ├── State └── Conceptual │ ├── Output.txt │ └── main.cc ├── Strategy └── Conceptual │ ├── Output.txt │ └── main.cc ├── TemplateMethod └── Conceptual │ ├── Output.txt │ └── main.cc └── Visitor ├── Conceptual ├── Output.txt └── main.cc └── RealWorld ├── Output.txt └── main.cc /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Mac", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [], 9 | "macFrameworkPath": [ 10 | "/System/Library/Frameworks", 11 | "/Library/Frameworks" 12 | ], 13 | "compilerPath": "/usr/bin/g++", 14 | "cStandard": "c11", 15 | "intelliSenseMode": "clang-x64", 16 | "compilerArgs": [], 17 | "cppStandard": "c++11" 18 | } 19 | ], 20 | "version": 4 21 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "g++", 6 | "type": "cppdbg", 7 | "request": "launch", 8 | "program": "${workspaceFolder}/bin/${fileBasenameNoExtension}", 9 | "args": ["-std=c++11"], 10 | "stopAtEntry": false, 11 | "cwd": "${workspaceFolder}", 12 | "environment": [], 13 | "externalConsole": false, 14 | "MIMode": "lldb", 15 | "preLaunchTask": "g++ build active file" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "shell", 6 | "label": "g++ build active file", 7 | "command": "/usr/bin/g++", 8 | "args": [ 9 | "-g", 10 | "-std=c++11", 11 | "${file}", 12 | "-o", 13 | "${workspaceFolder}/bin/${fileBasenameNoExtension}" 14 | ], 15 | "options": { 16 | "cwd": "/usr/bin" 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Refactoring.Guru: Design Patterns in C++ 2 | © Alexander Shvets 3 | 4 | This work is licensed under a 5 | Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. 6 | 7 | You should have received a copy of the license along with this 8 | work. If not, see . 9 | 10 | ======================================================================= 11 | 12 | Attribution-NonCommercial-NoDerivatives 4.0 International 13 | 14 | ======================================================================= 15 | 16 | Creative Commons Corporation ("Creative Commons") is not a law firm and 17 | does not provide legal services or legal advice. Distribution of 18 | Creative Commons public licenses does not create a lawyer-client or 19 | other relationship. Creative Commons makes its licenses and related 20 | information available on an "as-is" basis. Creative Commons gives no 21 | warranties regarding its licenses, any material licensed under their 22 | terms and conditions, or any related information. Creative Commons 23 | disclaims all liability for damages resulting from their use to the 24 | fullest extent possible. 25 | 26 | Using Creative Commons Public Licenses 27 | 28 | Creative Commons public licenses provide a standard set of terms and 29 | conditions that creators and other rights holders may use to share 30 | original works of authorship and other material subject to copyright 31 | and certain other rights specified in the public license below. The 32 | following considerations are for informational purposes only, are not 33 | exhaustive, and do not form part of our licenses. 34 | 35 | Considerations for licensors: Our public licenses are 36 | intended for use by those authorized to give the public 37 | permission to use material in ways otherwise restricted by 38 | copyright and certain other rights. Our licenses are 39 | irrevocable. Licensors should read and understand the terms 40 | and conditions of the license they choose before applying it. 41 | Licensors should also secure all rights necessary before 42 | applying our licenses so that the public can reuse the 43 | material as expected. Licensors should clearly mark any 44 | material not subject to the license. This includes other CC- 45 | licensed material, or material used under an exception or 46 | limitation to copyright. More considerations for licensors: 47 | wiki.creativecommons.org/Considerations_for_licensors 48 | 49 | Considerations for the public: By using one of our public 50 | licenses, a licensor grants the public permission to use the 51 | licensed material under specified terms and conditions. If 52 | the licensor's permission is not necessary for any reason--for 53 | example, because of any applicable exception or limitation to 54 | copyright--then that use is not regulated by the license. Our 55 | licenses grant only permissions under copyright and certain 56 | other rights that a licensor has authority to grant. Use of 57 | the licensed material may still be restricted for other 58 | reasons, including because others have copyright or other 59 | rights in the material. A licensor may make special requests, 60 | such as asking that all changes be marked or described. 61 | Although not required by our licenses, you are encouraged to 62 | respect those requests where reasonable. More_considerations 63 | for the public: 64 | wiki.creativecommons.org/Considerations_for_licensees 65 | 66 | ======================================================================= 67 | 68 | Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 69 | International Public License 70 | 71 | By exercising the Licensed Rights (defined below), You accept and agree 72 | to be bound by the terms and conditions of this Creative Commons 73 | Attribution-NonCommercial-NoDerivatives 4.0 International Public 74 | License ("Public License"). To the extent this Public License may be 75 | interpreted as a contract, You are granted the Licensed Rights in 76 | consideration of Your acceptance of these terms and conditions, and the 77 | Licensor grants You such rights in consideration of benefits the 78 | Licensor receives from making the Licensed Material available under 79 | these terms and conditions. 80 | 81 | 82 | Section 1 -- Definitions. 83 | 84 | a. Adapted Material means material subject to Copyright and Similar 85 | Rights that is derived from or based upon the Licensed Material 86 | and in which the Licensed Material is translated, altered, 87 | arranged, transformed, or otherwise modified in a manner requiring 88 | permission under the Copyright and Similar Rights held by the 89 | Licensor. For purposes of this Public License, where the Licensed 90 | Material is a musical work, performance, or sound recording, 91 | Adapted Material is always produced where the Licensed Material is 92 | synched in timed relation with a moving image. 93 | 94 | b. Copyright and Similar Rights means copyright and/or similar rights 95 | closely related to copyright including, without limitation, 96 | performance, broadcast, sound recording, and Sui Generis Database 97 | Rights, without regard to how the rights are labeled or 98 | categorized. For purposes of this Public License, the rights 99 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 100 | Rights. 101 | 102 | c. Effective Technological Measures means those measures that, in the 103 | absence of proper authority, may not be circumvented under laws 104 | fulfilling obligations under Article 11 of the WIPO Copyright 105 | Treaty adopted on December 20, 1996, and/or similar international 106 | agreements. 107 | 108 | d. Exceptions and Limitations means fair use, fair dealing, and/or 109 | any other exception or limitation to Copyright and Similar Rights 110 | that applies to Your use of the Licensed Material. 111 | 112 | e. Licensed Material means the artistic or literary work, database, 113 | or other material to which the Licensor applied this Public 114 | License. 115 | 116 | f. Licensed Rights means the rights granted to You subject to the 117 | terms and conditions of this Public License, which are limited to 118 | all Copyright and Similar Rights that apply to Your use of the 119 | Licensed Material and that the Licensor has authority to license. 120 | 121 | g. Licensor means the individual(s) or entity(ies) granting rights 122 | under this Public License. 123 | 124 | h. NonCommercial means not primarily intended for or directed towards 125 | commercial advantage or monetary compensation. For purposes of 126 | this Public License, the exchange of the Licensed Material for 127 | other material subject to Copyright and Similar Rights by digital 128 | file-sharing or similar means is NonCommercial provided there is 129 | no payment of monetary compensation in connection with the 130 | exchange. 131 | 132 | i. Share means to provide material to the public by any means or 133 | process that requires permission under the Licensed Rights, such 134 | as reproduction, public display, public performance, distribution, 135 | dissemination, communication, or importation, and to make material 136 | available to the public including in ways that members of the 137 | public may access the material from a place and at a time 138 | individually chosen by them. 139 | 140 | j. Sui Generis Database Rights means rights other than copyright 141 | resulting from Directive 96/9/EC of the European Parliament and of 142 | the Council of 11 March 1996 on the legal protection of databases, 143 | as amended and/or succeeded, as well as other essentially 144 | equivalent rights anywhere in the world. 145 | 146 | k. You means the individual or entity exercising the Licensed Rights 147 | under this Public License. Your has a corresponding meaning. 148 | 149 | 150 | Section 2 -- Scope. 151 | 152 | a. License grant. 153 | 154 | 1. Subject to the terms and conditions of this Public License, 155 | the Licensor hereby grants You a worldwide, royalty-free, 156 | non-sublicensable, non-exclusive, irrevocable license to 157 | exercise the Licensed Rights in the Licensed Material to: 158 | 159 | a. reproduce and Share the Licensed Material, in whole or 160 | in part, for NonCommercial purposes only; and 161 | 162 | b. produce and reproduce, but not Share, Adapted Material 163 | for NonCommercial purposes only. 164 | 165 | 2. Exceptions and Limitations. For the avoidance of doubt, where 166 | Exceptions and Limitations apply to Your use, this Public 167 | License does not apply, and You do not need to comply with 168 | its terms and conditions. 169 | 170 | 3. Term. The term of this Public License is specified in Section 171 | 6(a). 172 | 173 | 4. Media and formats; technical modifications allowed. The 174 | Licensor authorizes You to exercise the Licensed Rights in 175 | all media and formats whether now known or hereafter created, 176 | and to make technical modifications necessary to do so. The 177 | Licensor waives and/or agrees not to assert any right or 178 | authority to forbid You from making technical modifications 179 | necessary to exercise the Licensed Rights, including 180 | technical modifications necessary to circumvent Effective 181 | Technological Measures. For purposes of this Public License, 182 | simply making modifications authorized by this Section 2(a) 183 | (4) never produces Adapted Material. 184 | 185 | 5. Downstream recipients. 186 | 187 | a. Offer from the Licensor -- Licensed Material. Every 188 | recipient of the Licensed Material automatically 189 | receives an offer from the Licensor to exercise the 190 | Licensed Rights under the terms and conditions of this 191 | Public License. 192 | 193 | b. No downstream restrictions. You may not offer or impose 194 | any additional or different terms or conditions on, or 195 | apply any Effective Technological Measures to, the 196 | Licensed Material if doing so restricts exercise of the 197 | Licensed Rights by any recipient of the Licensed 198 | Material. 199 | 200 | 6. No endorsement. Nothing in this Public License constitutes or 201 | may be construed as permission to assert or imply that You 202 | are, or that Your use of the Licensed Material is, connected 203 | with, or sponsored, endorsed, or granted official status by, 204 | the Licensor or others designated to receive attribution as 205 | provided in Section 3(a)(1)(A)(i). 206 | 207 | b. Other rights. 208 | 209 | 1. Moral rights, such as the right of integrity, are not 210 | licensed under this Public License, nor are publicity, 211 | privacy, and/or other similar personality rights; however, to 212 | the extent possible, the Licensor waives and/or agrees not to 213 | assert any such rights held by the Licensor to the limited 214 | extent necessary to allow You to exercise the Licensed 215 | Rights, but not otherwise. 216 | 217 | 2. Patent and trademark rights are not licensed under this 218 | Public License. 219 | 220 | 3. To the extent possible, the Licensor waives any right to 221 | collect royalties from You for the exercise of the Licensed 222 | Rights, whether directly or through a collecting society 223 | under any voluntary or waivable statutory or compulsory 224 | licensing scheme. In all other cases the Licensor expressly 225 | reserves any right to collect such royalties, including when 226 | the Licensed Material is used other than for NonCommercial 227 | purposes. 228 | 229 | 230 | Section 3 -- License Conditions. 231 | 232 | Your exercise of the Licensed Rights is expressly made subject to the 233 | following conditions. 234 | 235 | a. Attribution. 236 | 237 | 1. If You Share the Licensed Material, You must: 238 | 239 | a. retain the following if it is supplied by the Licensor 240 | with the Licensed Material: 241 | 242 | i. identification of the creator(s) of the Licensed 243 | Material and any others designated to receive 244 | attribution, in any reasonable manner requested by 245 | the Licensor (including by pseudonym if 246 | designated); 247 | 248 | ii. a copyright notice; 249 | 250 | iii. a notice that refers to this Public License; 251 | 252 | iv. a notice that refers to the disclaimer of 253 | warranties; 254 | 255 | v. a URI or hyperlink to the Licensed Material to the 256 | extent reasonably practicable; 257 | 258 | b. indicate if You modified the Licensed Material and 259 | retain an indication of any previous modifications; and 260 | 261 | c. indicate the Licensed Material is licensed under this 262 | Public License, and include the text of, or the URI or 263 | hyperlink to, this Public License. 264 | 265 | For the avoidance of doubt, You do not have permission under 266 | this Public License to Share Adapted Material. 267 | 268 | 2. You may satisfy the conditions in Section 3(a)(1) in any 269 | reasonable manner based on the medium, means, and context in 270 | which You Share the Licensed Material. For example, it may be 271 | reasonable to satisfy the conditions by providing a URI or 272 | hyperlink to a resource that includes the required 273 | information. 274 | 275 | 3. If requested by the Licensor, You must remove any of the 276 | information required by Section 3(a)(1)(A) to the extent 277 | reasonably practicable. 278 | 279 | 280 | Section 4 -- Sui Generis Database Rights. 281 | 282 | Where the Licensed Rights include Sui Generis Database Rights that 283 | apply to Your use of the Licensed Material: 284 | 285 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 286 | to extract, reuse, reproduce, and Share all or a substantial 287 | portion of the contents of the database for NonCommercial purposes 288 | only and provided You do not Share Adapted Material; 289 | 290 | b. if You include all or a substantial portion of the database 291 | contents in a database in which You have Sui Generis Database 292 | Rights, then the database in which You have Sui Generis Database 293 | Rights (but not its individual contents) is Adapted Material; and 294 | 295 | c. You must comply with the conditions in Section 3(a) if You Share 296 | all or a substantial portion of the contents of the database. 297 | 298 | For the avoidance of doubt, this Section 4 supplements and does not 299 | replace Your obligations under this Public License where the Licensed 300 | Rights include other Copyright and Similar Rights. 301 | 302 | 303 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 304 | 305 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 306 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 307 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 308 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 309 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 310 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 311 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 312 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 313 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 314 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 315 | 316 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 317 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 318 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 319 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 320 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 321 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 322 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 323 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 324 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 325 | 326 | c. The disclaimer of warranties and limitation of liability provided 327 | above shall be interpreted in a manner that, to the extent 328 | possible, most closely approximates an absolute disclaimer and 329 | waiver of all liability. 330 | 331 | 332 | Section 6 -- Term and Termination. 333 | 334 | a. This Public License applies for the term of the Copyright and 335 | Similar Rights licensed here. However, if You fail to comply with 336 | this Public License, then Your rights under this Public License 337 | terminate automatically. 338 | 339 | b. Where Your right to use the Licensed Material has terminated under 340 | Section 6(a), it reinstates: 341 | 342 | 1. automatically as of the date the violation is cured, provided 343 | it is cured within 30 days of Your discovery of the 344 | violation; or 345 | 346 | 2. upon express reinstatement by the Licensor. 347 | 348 | For the avoidance of doubt, this Section 6(b) does not affect any 349 | right the Licensor may have to seek remedies for Your violations 350 | of this Public License. 351 | 352 | c. For the avoidance of doubt, the Licensor may also offer the 353 | Licensed Material under separate terms or conditions or stop 354 | distributing the Licensed Material at any time; however, doing so 355 | will not terminate this Public License. 356 | 357 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 358 | License. 359 | 360 | 361 | Section 7 -- Other Terms and Conditions. 362 | 363 | a. The Licensor shall not be bound by any additional or different 364 | terms or conditions communicated by You unless expressly agreed. 365 | 366 | b. Any arrangements, understandings, or agreements regarding the 367 | Licensed Material not stated herein are separate from and 368 | independent of the terms and conditions of this Public License. 369 | 370 | 371 | Section 8 -- Interpretation. 372 | 373 | a. For the avoidance of doubt, this Public License does not, and 374 | shall not be interpreted to, reduce, limit, restrict, or impose 375 | conditions on any use of the Licensed Material that could lawfully 376 | be made without permission under this Public License. 377 | 378 | b. To the extent possible, if any provision of this Public License is 379 | deemed unenforceable, it shall be automatically reformed to the 380 | minimum extent necessary to make it enforceable. If the provision 381 | cannot be reformed, it shall be severed from this Public License 382 | without affecting the enforceability of the remaining terms and 383 | conditions. 384 | 385 | c. No term or condition of this Public License will be waived and no 386 | failure to comply consented to unless expressly agreed to by the 387 | Licensor. 388 | 389 | d. Nothing in this Public License constitutes or may be interpreted 390 | as a limitation upon, or waiver of, any privileges and immunities 391 | that apply to the Licensor or You, including from the legal 392 | processes of any jurisdiction or authority. 393 | 394 | ======================================================================= 395 | 396 | Creative Commons is not a party to its public 397 | licenses. Notwithstanding, Creative Commons may elect to apply one of 398 | its public licenses to material it publishes and in those instances 399 | will be considered the “Licensor.” The text of the Creative Commons 400 | public licenses is dedicated to the public domain under the CC0 Public 401 | Domain Dedication. Except for the limited purpose of indicating that 402 | material is shared under a Creative Commons public license or as 403 | otherwise permitted by the Creative Commons policies published at 404 | creativecommons.org/policies, Creative Commons does not authorize the 405 | use of the trademark "Creative Commons" or any other trademark or logo 406 | of Creative Commons without its prior written consent including, 407 | without limitation, in connection with any unauthorized modifications 408 | to any of its public licenses or any other arrangements, 409 | understandings, or agreements concerning use of licensed material. For 410 | the avoidance of doubt, this paragraph does not form part of the 411 | public licenses. 412 | 413 | Creative Commons may be contacted at creativecommons.org. 414 | 415 | 416 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns in C++ 2 | 3 | This repository is part of the [Refactoring.Guru](https://refactoring.guru/design-patterns) project. 4 | 5 | It contains C++ examples for all classic GoF design patterns. Each pattern includes two examples: 6 | 7 | - [x] **Conceptual** examples show the internal structure of patterns with detailed comments. 8 | - [ ] **RealWorld** examples show how the patterns can be used in a real-world C++ application. 9 | 10 | 11 | ## Requirements 12 | 13 | The examples were written as cross platform console application using c++17. It means that you should be able to compile and execute those examples with any recent compiler. 14 | 15 | we recommend working with Visual Studio Code because it is a lightweight and cross-platform tool .It is a very complete IDE and is available for free (https://code.visualstudio.com/). You may need to install c++ extension and the compiler you prefer (The extension is still in preview and its focus is code editing, navigation, and debugging support for C and C++). For more information on how to use VSCode with c++ refer to: https://code.visualstudio.com/docs/languages/cpp . 16 | 17 | For code execution in VSCode you will need to set up your task first. An example using g++ : 18 | 19 | ```sh 20 | { 21 | "version": "2.0.0", 22 | "tasks": [ 23 | { 24 | "label": "build", 25 | "type": "shell", 26 | "command": "g++ -g -std=c++17 Conceptual/main.cc -o main", 27 | "group":{ 28 | "kind": "build", 29 | "isDefault": true 30 | }, 31 | "problemMatcher":"$gcc" 32 | } 33 | ] 34 | } 35 | ``` 36 | Then you just need to start the executable. In case you have some doubts here you have an useful [tutorial] using vscode. 37 | 38 | ## Contributor's Guide 39 | 40 | I appreciate any help, whether it's a simple fix of a typo or a whole new example. Just make a fork, make your change and submit a pull request. 41 | 42 | Here's a style guide which might help you to keep your changes consistent with the rest of the project's code: 43 | 44 | 1. All code should match the [Google style guide]. 45 | 2. Aim to put all code within one .cc file. Yes, I realize that it's not how it supposed to be done in production. However, it helps people to understand examples better, since all code fits into one screen. 46 | 3. The comments doesn't follow the style guide for compatibility reasons with other language examples. 47 | 48 | 49 | 50 | ## License 51 | 52 | This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. 53 | 54 | Creative Commons License 55 | 56 | 57 | 58 | [Google style guide]: 59 | [tutorial]: 60 | -------------------------------------------------------------------------------- /bin/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /src/AbstractFactory/Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | 2 | Client: Testing client code with the first factory type: 3 | The result of the product B1. 4 | The result of the B1 collaborating with the (The result of the product A1.) 5 | 6 | Client: Testing the same client code with the second factory type: 7 | The result of the product B2. 8 | The result of the B2 collaborating with the (The result of the product A2.) -------------------------------------------------------------------------------- /src/AbstractFactory/Conceptual/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /** 5 | * EN: Abstract Factory Design Pattern 6 | * 7 | * Intent: Lets you produce families of related objects without specifying their 8 | * concrete classes. 9 | * 10 | * RU: Паттерн Абстрактная Фабрика 11 | * 12 | * Назначение: Предоставляет интерфейс для создания семейств связанных или 13 | * зависимых объектов без привязки к их конкретным классам. 14 | */ 15 | 16 | /** 17 | * EN: Each distinct product of a product family should have a base interface. 18 | * All variants of the product must implement this interface. 19 | * 20 | * RU: Каждый отдельный продукт семейства продуктов должен иметь базовый 21 | * интерфейс. Все вариации продукта должны реализовывать этот интерфейс. 22 | */ 23 | class AbstractProductA { 24 | public: 25 | virtual ~AbstractProductA(){}; 26 | virtual std::string UsefulFunctionA() const = 0; 27 | }; 28 | 29 | /** 30 | * EN: Concrete Products are created by corresponding Concrete Factories. 31 | * 32 | * RU: Конкретные продукты создаются соответствующими Конкретными Фабриками. 33 | */ 34 | class ConcreteProductA1 : public AbstractProductA { 35 | public: 36 | std::string UsefulFunctionA() const override { 37 | return "The result of the product A1."; 38 | } 39 | }; 40 | 41 | class ConcreteProductA2 : public AbstractProductA { 42 | std::string UsefulFunctionA() const override { 43 | return "The result of the product A2."; 44 | } 45 | }; 46 | 47 | /** 48 | * EN: Here's the the base interface of another product. All products can 49 | * interact with each other, but proper interaction is possible only between 50 | * products of the same concrete variant. 51 | * 52 | * RU: Базовый интерфейс другого продукта. Все продукты могут взаимодействовать 53 | * друг с другом, но правильное взаимодействие возможно только между продуктами 54 | * одной и той же конкретной вариации. 55 | */ 56 | class AbstractProductB { 57 | /** 58 | * EN: Product B is able to do its own thing... 59 | * 60 | * RU: Продукт B способен работать самостоятельно... 61 | */ 62 | public: 63 | virtual ~AbstractProductB(){}; 64 | virtual std::string UsefulFunctionB() const = 0; 65 | /** 66 | * EN: ...but it also can collaborate with the ProductA. 67 | * 68 | * The Abstract Factory makes sure that all products it creates are of the 69 | * same variant and thus, compatible. 70 | * 71 | * RU: ...а также взаимодействовать с Продуктами A той же вариации. 72 | * 73 | * Абстрактная Фабрика гарантирует, что все продукты, которые она создает, 74 | * имеют одинаковую вариацию и, следовательно, совместимы. 75 | */ 76 | virtual std::string AnotherUsefulFunctionB(const AbstractProductA &collaborator) const = 0; 77 | }; 78 | 79 | /** 80 | * EN: Concrete Products are created by corresponding Concrete Factories. 81 | * 82 | * RU: Конкретные Продукты создаются соответствующими Конкретными Фабриками. 83 | */ 84 | class ConcreteProductB1 : public AbstractProductB { 85 | public: 86 | std::string UsefulFunctionB() const override { 87 | return "The result of the product B1."; 88 | } 89 | /** 90 | * EN: The variant, Product B1, is only able to work correctly with the 91 | * variant, Product A1. Nevertheless, it accepts any instance of 92 | * AbstractProductA as an argument. 93 | * 94 | * RU: Продукт B1 может корректно работать только с Продуктом A1. Тем не 95 | * менее, он принимает любой экземпляр Абстрактного Продукта А в качестве 96 | * аргумента. 97 | */ 98 | std::string AnotherUsefulFunctionB(const AbstractProductA &collaborator) const override { 99 | const std::string result = collaborator.UsefulFunctionA(); 100 | return "The result of the B1 collaborating with ( " + result + " )"; 101 | } 102 | }; 103 | 104 | class ConcreteProductB2 : public AbstractProductB { 105 | public: 106 | std::string UsefulFunctionB() const override { 107 | return "The result of the product B2."; 108 | } 109 | /** 110 | * EN: The variant, Product B2, is only able to work correctly with the 111 | * variant, Product A2. Nevertheless, it accepts any instance of 112 | * AbstractProductA as an argument. 113 | * 114 | * RU: Продукт B2 может корректно работать только с Продуктом A2. Тем не 115 | * менее, он принимает любой экземпляр Абстрактного Продукта А в качестве 116 | * аргумента. 117 | */ 118 | std::string AnotherUsefulFunctionB(const AbstractProductA &collaborator) const override { 119 | const std::string result = collaborator.UsefulFunctionA(); 120 | return "The result of the B2 collaborating with ( " + result + " )"; 121 | } 122 | }; 123 | 124 | /** 125 | * EN: The Abstract Factory interface declares a set of methods that return 126 | * different abstract products. These products are called a family and are 127 | * related by a high-level theme or concept. Products of one family are usually 128 | * able to collaborate among themselves. A family of products may have several 129 | * variants, but the products of one variant are incompatible with products of 130 | * another. 131 | * 132 | * RU: Интерфейс Абстрактной Фабрики объявляет набор методов, которые возвращают 133 | * различные абстрактные продукты. Эти продукты называются семейством и связаны 134 | * темой или концепцией высокого уровня. Продукты одного семейства обычно могут 135 | * взаимодействовать между собой. Семейство продуктов может иметь несколько 136 | * вариаций, но продукты одной вариации несовместимы с продуктами другой. 137 | */ 138 | class AbstractFactory { 139 | public: 140 | virtual AbstractProductA *CreateProductA() const = 0; 141 | virtual AbstractProductB *CreateProductB() const = 0; 142 | }; 143 | 144 | /** 145 | * EN: Concrete Factories produce a family of products that belong to a single 146 | * variant. The factory guarantees that resulting products are compatible. Note 147 | * that signatures of the Concrete Factory's methods return an abstract product, 148 | * while inside the method a concrete product is instantiated. 149 | * 150 | * RU: Конкретная Фабрика производит семейство продуктов одной вариации. Фабрика 151 | * гарантирует совместимость полученных продуктов. Обратите внимание, что 152 | * сигнатуры методов Конкретной Фабрики возвращают абстрактный продукт, в то 153 | * время как внутри метода создается экземпляр конкретного продукта. 154 | */ 155 | class ConcreteFactory1 : public AbstractFactory { 156 | public: 157 | AbstractProductA *CreateProductA() const override { 158 | return new ConcreteProductA1(); 159 | } 160 | AbstractProductB *CreateProductB() const override { 161 | return new ConcreteProductB1(); 162 | } 163 | }; 164 | 165 | /** 166 | * EN: Each Concrete Factory has a corresponding product variant. 167 | * 168 | * RU: Каждая Конкретная Фабрика имеет соответствующую вариацию продукта. 169 | */ 170 | class ConcreteFactory2 : public AbstractFactory { 171 | public: 172 | AbstractProductA *CreateProductA() const override { 173 | return new ConcreteProductA2(); 174 | } 175 | AbstractProductB *CreateProductB() const override { 176 | return new ConcreteProductB2(); 177 | } 178 | }; 179 | 180 | /** 181 | * EN: The client code works with factories and products only through abstract 182 | * types: AbstractFactory and AbstractProduct. This lets you pass any factory or 183 | * product subclass to the client code without breaking it. 184 | * 185 | * RU: Клиентский код работает с фабриками и продуктами только через абстрактные 186 | * типы: Абстрактная Фабрика и Абстрактный Продукт. Это позволяет передавать 187 | * любой подкласс фабрики или продукта клиентскому коду, не нарушая его. 188 | */ 189 | 190 | void ClientCode(const AbstractFactory &factory) { 191 | const AbstractProductA *product_a = factory.CreateProductA(); 192 | const AbstractProductB *product_b = factory.CreateProductB(); 193 | std::cout << product_b->UsefulFunctionB() << "\n"; 194 | std::cout << product_b->AnotherUsefulFunctionB(*product_a) << "\n"; 195 | delete product_a; 196 | delete product_b; 197 | } 198 | 199 | int main() { 200 | std::cout << "Client: Testing client code with the first factory type:\n"; 201 | ConcreteFactory1 *f1 = new ConcreteFactory1(); 202 | ClientCode(*f1); 203 | delete f1; 204 | std::cout << std::endl; 205 | std::cout << "Client: Testing the same client code with the second factory type:\n"; 206 | ConcreteFactory2 *f2 = new ConcreteFactory2(); 207 | ClientCode(*f2); 208 | delete f2; 209 | return 0; 210 | } -------------------------------------------------------------------------------- /src/Adapter/Conceptual/MultipleInheritance/Output.txt: -------------------------------------------------------------------------------- 1 | Client: I can work just fine with the Target objects: 2 | Target: The default target's behavior. 3 | 4 | Client: The Adaptee class has a weird interface. See, I don't understand it: 5 | Adaptee: .eetpadA eht fo roivaheb laicepS 6 | 7 | Client: But I can work with it via the Adapter: 8 | Adapter: (TRANSLATED) Special behavior of the Adaptee. 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Adapter/Conceptual/MultipleInheritance/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /** 6 | * EN: Adapter Design Pattern 7 | * 8 | * Intent: Provides a unified interface that allows objects with incompatible 9 | * interfaces to collaborate. 10 | * 11 | * RU: Паттерн Адаптер 12 | * 13 | * Назначение: Позволяет объектам с несовместимыми интерфейсами работать вместе. 14 | */ 15 | 16 | /** 17 | * EN: The Target defines the domain-specific interface used by the client code. 18 | * 19 | * RU: Целевой класс объявляет интерфейс, с которым может работать клиентский 20 | * код. 21 | */ 22 | class Target { 23 | public: 24 | virtual ~Target() = default; 25 | virtual std::string Request() const { 26 | return "Target: The default target's behavior."; 27 | } 28 | }; 29 | 30 | /** 31 | * EN: The Adaptee contains some useful behavior, but its interface is 32 | * incompatible with the existing client code. The Adaptee needs some adaptation 33 | * before the client code can use it. 34 | * 35 | * RU: Адаптируемый класс содержит некоторое полезное поведение, но его 36 | * интерфейс несовместим с существующим клиентским кодом. Адаптируемый класс 37 | * нуждается в некоторой доработке, прежде чем клиентский код сможет его 38 | * использовать. 39 | */ 40 | class Adaptee { 41 | public: 42 | std::string SpecificRequest() const { 43 | return ".eetpadA eht fo roivaheb laicepS"; 44 | } 45 | }; 46 | 47 | /** 48 | * EN: The Adapter makes the Adaptee's interface compatible with the Target's 49 | * interface using multiple inheritance. 50 | * 51 | * RU: Адаптер делает интерфейс Адаптируемого класса совместимым с целевым 52 | * интерфейсом с помощью множественного наследования. 53 | */ 54 | class Adapter : public Target, public Adaptee { 55 | public: 56 | Adapter() {} 57 | std::string Request() const override { 58 | std::string to_reverse = SpecificRequest(); 59 | std::reverse(to_reverse.begin(), to_reverse.end()); 60 | return "Adapter: (TRANSLATED) " + to_reverse; 61 | } 62 | }; 63 | 64 | /** 65 | * EN: The client code supports all classes that follow the Target interface. 66 | * 67 | * RU: Клиентский код поддерживает все классы, использующие целевой интерфейс. 68 | */ 69 | void ClientCode(const Target *target) { 70 | std::cout << target->Request(); 71 | } 72 | 73 | int main() { 74 | std::cout << "Client: I can work just fine with the Target objects:\n"; 75 | Target *target = new Target; 76 | ClientCode(target); 77 | std::cout << "\n\n"; 78 | Adaptee *adaptee = new Adaptee; 79 | std::cout << "Client: The Adaptee class has a weird interface. See, I don't understand it:\n"; 80 | std::cout << "Adaptee: " << adaptee->SpecificRequest(); 81 | std::cout << "\n\n"; 82 | std::cout << "Client: But I can work with it via the Adapter:\n"; 83 | Adapter *adapter = new Adapter; 84 | ClientCode(adapter); 85 | std::cout << "\n"; 86 | 87 | delete target; 88 | delete adaptee; 89 | delete adapter; 90 | 91 | return 0; 92 | } 93 | -------------------------------------------------------------------------------- /src/Adapter/Conceptual/Normal/Output.txt: -------------------------------------------------------------------------------- 1 | Client: I can work just fine with the Target objects: 2 | Target: The default target's behavior. 3 | 4 | Client: The Adaptee class has a weird interface. See, I don't understand it: 5 | Adaptee: .eetpadA eht fo roivaheb laicepS 6 | 7 | Client: But I can work with it via the Adapter: 8 | Adapter: (TRANSLATED) Special behavior of the Adaptee. 9 | -------------------------------------------------------------------------------- /src/Adapter/Conceptual/Normal/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /** 6 | * EN: Adapter Design Pattern 7 | * 8 | * Intent: Provides a unified interface that allows objects with incompatible 9 | * interfaces to collaborate. 10 | * 11 | * RU: Паттерн Адаптер 12 | * 13 | * Назначение: Позволяет объектам с несовместимыми интерфейсами работать вместе. 14 | */ 15 | 16 | /** 17 | * EN: The Target defines the domain-specific interface used by the client code. 18 | * 19 | * RU: Целевой класс объявляет интерфейс, с которым может работать клиентский 20 | * код. 21 | */ 22 | class Target { 23 | public: 24 | virtual ~Target() = default; 25 | 26 | virtual std::string Request() const { 27 | return "Target: The default target's behavior."; 28 | } 29 | }; 30 | 31 | /** 32 | * EN: The Adaptee contains some useful behavior, but its interface is 33 | * incompatible with the existing client code. The Adaptee needs some adaptation 34 | * before the client code can use it. 35 | * 36 | * RU: Адаптируемый класс содержит некоторое полезное поведение, но его 37 | * интерфейс несовместим с существующим клиентским кодом. Адаптируемый класс 38 | * нуждается в некоторой доработке, прежде чем клиентский код сможет его 39 | * использовать. 40 | */ 41 | class Adaptee { 42 | public: 43 | std::string SpecificRequest() const { 44 | return ".eetpadA eht fo roivaheb laicepS"; 45 | } 46 | }; 47 | 48 | /** 49 | * EN: The Adapter makes the Adaptee's interface compatible with the Target's 50 | * interface. 51 | * 52 | * RU: Адаптер делает интерфейс Адаптируемого класса совместимым с целевым 53 | * интерфейсом. 54 | */ 55 | class Adapter : public Target { 56 | private: 57 | Adaptee *adaptee_; 58 | 59 | public: 60 | Adapter(Adaptee *adaptee) : adaptee_(adaptee) {} 61 | std::string Request() const override { 62 | std::string to_reverse = this->adaptee_->SpecificRequest(); 63 | std::reverse(to_reverse.begin(), to_reverse.end()); 64 | return "Adapter: (TRANSLATED) " + to_reverse; 65 | } 66 | }; 67 | 68 | /** 69 | * EN: The client code supports all classes that follow the Target interface. 70 | * 71 | * RU: Клиентский код поддерживает все классы, использующие целевой интерфейс. 72 | */ 73 | void ClientCode(const Target *target) { 74 | std::cout << target->Request(); 75 | } 76 | 77 | int main() { 78 | std::cout << "Client: I can work just fine with the Target objects:\n"; 79 | Target *target = new Target; 80 | ClientCode(target); 81 | std::cout << "\n\n"; 82 | Adaptee *adaptee = new Adaptee; 83 | std::cout << "Client: The Adaptee class has a weird interface. See, I don't understand it:\n"; 84 | std::cout << "Adaptee: " << adaptee->SpecificRequest(); 85 | std::cout << "\n\n"; 86 | std::cout << "Client: But I can work with it via the Adapter:\n"; 87 | Adapter *adapter = new Adapter(adaptee); 88 | ClientCode(adapter); 89 | std::cout << "\n"; 90 | 91 | delete target; 92 | delete adaptee; 93 | delete adapter; 94 | 95 | return 0; 96 | } 97 | -------------------------------------------------------------------------------- /src/Bridge/Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Abstraction: Base operation with: 2 | ConcreteImplementationA: Here's the result on the platform A. 3 | 4 | ExtendedAbstraction: Extended operation with: 5 | ConcreteImplementationB: Here's the result on the platform B. 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Bridge/Conceptual/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /** 5 | * EN: Bridge Design Pattern 6 | * 7 | * Intent: Lets you split a large class or a set of closely related classes into 8 | * two separate hierarchies—abstraction and implementation—which can be 9 | * developed independently of each other. 10 | * 11 | * A 12 | * / \ A N 13 | * Aa Ab ===> / \ / \ 14 | * / \ / \ Aa(N) Ab(N) 1 2 15 | * Aa1 Aa2 Ab1 Ab2 16 | * 17 | * RU: Паттерн Мост 18 | * 19 | * Назначение: Разделяет один или несколько классов на две отдельные иерархии — 20 | * абстракцию и реализацию, позволяя изменять их независимо друг от друга. 21 | * 22 | * A 23 | * / \ A N 24 | * Aa Ab ===> / \ / \ 25 | * / \ / \ Aa(N) Ab(N) 1 2 26 | * Aa1 Aa2 Ab1 Ab2 27 | */ 28 | 29 | /** 30 | * EN: The Implementation defines the interface for all implementation classes. 31 | * It doesn't have to match the Abstraction's interface. In fact, the two 32 | * interfaces can be entirely different. Typically the Implementation interface 33 | * provides only primitive operations, while the Abstraction defines higher- 34 | * level operations based on those primitives. 35 | * 36 | * RU: Реализация устанавливает интерфейс для всех классов реализации. Он не 37 | * должен соответствовать интерфейсу Абстракции. На практике оба интерфейса 38 | * могут быть совершенно разными. Как правило, интерфейс Реализации 39 | * предоставляет только примитивные операции, в то время как Абстракция 40 | * определяет операции более высокого уровня, основанные на этих примитивах. 41 | */ 42 | 43 | class Implementation { 44 | public: 45 | virtual ~Implementation() {} 46 | virtual std::string OperationImplementation() const = 0; 47 | }; 48 | 49 | /** 50 | * EN: Each Concrete Implementation corresponds to a specific platform and 51 | * implements the Implementation interface using that platform's API. 52 | * 53 | * RU: Каждая Конкретная Реализация соответствует определённой платформе и 54 | * реализует интерфейс Реализации с использованием API этой платформы. 55 | */ 56 | class ConcreteImplementationA : public Implementation { 57 | public: 58 | std::string OperationImplementation() const override { 59 | return "ConcreteImplementationA: Here's the result on the platform A.\n"; 60 | } 61 | }; 62 | class ConcreteImplementationB : public Implementation { 63 | public: 64 | std::string OperationImplementation() const override { 65 | return "ConcreteImplementationB: Here's the result on the platform B.\n"; 66 | } 67 | }; 68 | 69 | /** 70 | * EN: The Abstraction defines the interface for the "control" part of the two 71 | * class hierarchies. It maintains a reference to an object of the 72 | * Implementation hierarchy and delegates all of the real work to this object. 73 | * 74 | * RU: Абстракция устанавливает интерфейс для «управляющей» части двух иерархий 75 | * классов. Она содержит ссылку на объект из иерархии Реализации и делегирует 76 | * ему всю настоящую работу. 77 | */ 78 | 79 | class Abstraction { 80 | /** 81 | * @var Implementation 82 | */ 83 | protected: 84 | Implementation* implementation_; 85 | 86 | public: 87 | Abstraction(Implementation* implementation) : implementation_(implementation) { 88 | } 89 | 90 | virtual ~Abstraction() { 91 | } 92 | 93 | virtual std::string Operation() const { 94 | return "Abstraction: Base operation with:\n" + 95 | this->implementation_->OperationImplementation(); 96 | } 97 | }; 98 | /** 99 | * EN: You can extend the Abstraction without changing the Implementation 100 | * classes. 101 | * 102 | * RU: Можно расширить Абстракцию без изменения классов Реализации. 103 | */ 104 | class ExtendedAbstraction : public Abstraction { 105 | public: 106 | ExtendedAbstraction(Implementation* implementation) : Abstraction(implementation) { 107 | } 108 | std::string Operation() const override { 109 | return "ExtendedAbstraction: Extended operation with:\n" + 110 | this->implementation_->OperationImplementation(); 111 | } 112 | }; 113 | 114 | /** 115 | * EN: Except for the initialization phase, where an Abstraction object gets 116 | * linked with a specific Implementation object, the client code should only 117 | * depend on the Abstraction class. This way the client code can support any 118 | * abstraction-implementation combination. 119 | * 120 | * RU: За исключением этапа инициализации, когда объект Абстракции связывается с 121 | * определённым объектом Реализации, клиентский код должен зависеть только от 122 | * класса Абстракции. Таким образом, клиентский код может поддерживать любую 123 | * комбинацию абстракции и реализации. 124 | */ 125 | void ClientCode(const Abstraction& abstraction) { 126 | // ... 127 | std::cout << abstraction.Operation(); 128 | // ... 129 | } 130 | /** 131 | * EN: The client code should be able to work with any pre-configured 132 | * abstraction-implementation combination. 133 | * 134 | * RU: Клиентский код должен работать с любой предварительно сконфигурированной 135 | * комбинацией абстракции и реализации. 136 | */ 137 | 138 | int main() { 139 | Implementation* implementation = new ConcreteImplementationA; 140 | Abstraction* abstraction = new Abstraction(implementation); 141 | ClientCode(*abstraction); 142 | std::cout << std::endl; 143 | delete implementation; 144 | delete abstraction; 145 | 146 | implementation = new ConcreteImplementationB; 147 | abstraction = new ExtendedAbstraction(implementation); 148 | ClientCode(*abstraction); 149 | 150 | delete implementation; 151 | delete abstraction; 152 | 153 | return 0; 154 | } 155 | -------------------------------------------------------------------------------- /src/Builder/Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Standard basic product: 2 | Product parts: PartA1 3 | 4 | Standard full featured product: 5 | Product parts: PartA1, PartB1, PartC1 6 | 7 | Custom product: 8 | Product parts: PartA1, PartC1 9 | 10 | -------------------------------------------------------------------------------- /src/Builder/Conceptual/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /** 6 | * EN: Builder Design Pattern 7 | * 8 | * Intent: Lets you construct complex objects step by step. The pattern allows 9 | * you to produce different types and representations of an object using the 10 | * same construction code. 11 | * 12 | * RU: Паттерн Строитель 13 | * 14 | * Назначение: Позволяет создавать сложные объекты пошагово. Строитель даёт 15 | * возможность использовать один и тот же код строительства для получения разных 16 | * представлений объектов. 17 | */ 18 | 19 | /** 20 | * EN: It makes sense to use the Builder pattern only when your products are 21 | * quite complex and require extensive configuration. 22 | * 23 | * Unlike in other creational patterns, different concrete builders can produce 24 | * unrelated products. In other words, results of various builders may not 25 | * always follow the same interface. 26 | * 27 | * RU: Имеет смысл использовать паттерн Строитель только тогда, когда ваши 28 | * продукты достаточно сложны и требуют обширной конфигурации. 29 | * 30 | * В отличие от других порождающих паттернов, различные конкретные строители 31 | * могут производить несвязанные продукты. Другими словами, результаты различных 32 | * строителей могут не всегда следовать одному и тому же интерфейсу. 33 | */ 34 | 35 | class Product1{ 36 | public: 37 | std::vector parts_; 38 | void ListParts()const{ 39 | std::cout << "Product parts: "; 40 | for (size_t i=0;iReset(); 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 << "\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 | } --------------------------------------------------------------------------------