├── .github └── workflows │ └── test.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── behavioral ├── README.md ├── chain-of-responsibility │ ├── Cargo.toml │ ├── README.md │ ├── department.rs │ ├── department │ │ ├── cashier.rs │ │ ├── doctor.rs │ │ ├── medical.rs │ │ └── reception.rs │ ├── main.rs │ └── patient.rs ├── command │ ├── Cargo.toml │ ├── README.md │ ├── command.rs │ ├── command │ │ ├── copy.rs │ │ ├── cut.rs │ │ └── paste.rs │ ├── main.rs │ └── res │ │ └── editor.png ├── iterator │ ├── Cargo.toml │ ├── README.md │ ├── main.rs │ └── users.rs ├── mediator │ ├── README.md │ ├── images │ │ ├── mediator-mut-problem.png │ │ ├── mediator-rust-approach.jpg │ │ ├── problem.png │ │ └── solution.png │ ├── mediator-rc-refcell │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── main.rs │ │ ├── train_station.rs │ │ └── trains │ │ │ ├── freight_train.rs │ │ │ ├── mod.rs │ │ │ └── passenger_train.rs │ └── mediator-top-down │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── main.rs │ │ ├── train_station.rs │ │ └── trains │ │ ├── freight_train.rs │ │ ├── mod.rs │ │ └── passenger_train.rs ├── memento │ ├── Cargo.toml │ ├── README.md │ ├── conceptual.rs │ └── serde.rs ├── observer │ ├── Cargo.toml │ ├── README.md │ ├── editor.rs │ ├── main.rs │ └── observer.rs ├── state │ ├── Cargo.toml │ ├── README.md │ ├── images │ │ ├── playing.png │ │ ├── state_machine.jpg │ │ └── stopped.png │ ├── main.rs │ ├── player.rs │ └── state.rs ├── strategy │ ├── Cargo.toml │ ├── README.md │ ├── conceptual.rs │ └── functional.rs ├── template-method │ ├── Cargo.toml │ ├── README.md │ └── main.rs └── visitor │ ├── Cargo.toml │ ├── README.md │ ├── main.rs │ └── visitor.rs ├── creational ├── README.md ├── abstract-factory │ ├── README.md │ ├── app-dyn │ │ ├── Cargo.toml │ │ ├── main.rs │ │ └── render.rs │ ├── app │ │ ├── Cargo.toml │ │ ├── main.rs │ │ └── render.rs │ ├── gui │ │ ├── Cargo.toml │ │ └── lib.rs │ ├── macos-gui │ │ ├── Cargo.toml │ │ ├── button.rs │ │ ├── checkbox.rs │ │ ├── factory.rs │ │ └── lib.rs │ └── windows-gui │ │ ├── Cargo.toml │ │ ├── button.rs │ │ ├── checkbox.rs │ │ ├── factory.rs │ │ └── lib.rs ├── builder │ ├── Cargo.toml │ ├── README.md │ ├── builders │ │ ├── car.rs │ │ ├── car_manual.rs │ │ └── mod.rs │ ├── cars │ │ ├── car.rs │ │ ├── manual.rs │ │ └── mod.rs │ ├── components.rs │ ├── director.rs │ └── main.rs ├── factory-method │ ├── README.md │ ├── maze-game │ │ ├── Cargo.toml │ │ ├── game.rs │ │ ├── magic_maze.rs │ │ ├── main.rs │ │ └── ordinary_maze.rs │ └── render-dialog │ │ ├── Cargo.toml │ │ ├── gui.rs │ │ ├── html_gui.rs │ │ ├── init.rs │ │ ├── main.rs │ │ └── windows_gui.rs ├── prototype │ ├── Cargo.toml │ ├── README.md │ └── main.rs ├── simple-factory │ ├── Cargo.toml │ ├── README.md │ ├── button │ │ ├── id.rs │ │ ├── mod.rs │ │ └── title.rs │ └── main.rs ├── singleton │ ├── README.md │ ├── how-to-create │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── lazy.rs │ │ ├── local.rs │ │ ├── mutex.rs │ │ └── once.rs │ └── logger │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── app.rs │ │ ├── log.rs │ │ ├── main.rs │ │ └── simple_logger.rs └── static-creation-method │ ├── Cargo.toml │ ├── README.md │ └── main.rs └── structural ├── README.md ├── adapter ├── Cargo.toml ├── README.md ├── adaptee.rs ├── adapter.rs ├── main.rs └── target.rs ├── bridge ├── Cargo.toml ├── README.md ├── device │ ├── mod.rs │ ├── radio.rs │ └── tv.rs ├── main.rs └── remotes │ ├── advanced.rs │ ├── basic.rs │ └── mod.rs ├── composite ├── Cargo.toml ├── README.md ├── fs │ ├── file.rs │ ├── folder.rs │ └── mod.rs └── main.rs ├── decorator ├── Cargo.toml ├── README.md └── main.rs ├── facade ├── Cargo.toml ├── README.md ├── account.rs ├── ledger.rs ├── main.rs ├── notification.rs ├── security_code.rs ├── wallet.rs └── wallet_facade.rs ├── flyweight ├── Cargo.toml ├── README.md ├── forest.rs ├── forest │ └── tree.rs ├── main.rs └── res │ └── forest.svg └── proxy ├── Cargo.toml ├── README.md ├── main.rs ├── server.rs └── server ├── application.rs └── nginx.rs /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test All Patterns 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | pull_request: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Use Rust 1.53 19 | run: rustup install 1.53 20 | - name: Run Rustfmt 21 | run: cargo fmt -- --check 22 | - name: Run Clippy 23 | run: cargo clippy -- -D clippy::all 24 | - run: cargo run --bin chain-of-responsibility 25 | - run: cargo build --bin command # TUI. It can run on the local machine. 26 | - run: cargo run --bin iterator 27 | - run: cargo run --bin mediator-top-down 28 | - run: cargo run --bin mediator-rc-refcell 29 | - run: cargo run --bin memento 30 | - run: cargo run --bin memento-serde 31 | - run: cargo run --bin observer 32 | - run: cargo build --bin state # TUI. It can run on the local machine. 33 | - run: cargo run --bin strategy 34 | - run: cargo run --bin strategy-func 35 | - run: cargo run --bin template-method 36 | - run: cargo run --bin visitor 37 | - run: cargo run --bin abstract-factory 38 | - run: cargo run --bin abstract-factory-dyn 39 | - run: cargo run --bin builder 40 | - run: cargo run --bin factory-method-maze-game 41 | - run: cargo run --bin factory-method-render-dialog 42 | - run: cargo run --bin prototype 43 | - run: cargo run --bin simple-factory 44 | - run: cargo run --bin singleton-local 45 | - run: cargo run --bin singleton-lazy 46 | # - run: cargo run --bin singleton-mutex # Requires Rust 1.63 47 | - run: cargo run --bin singleton-once 48 | - run: cargo run --bin singleton-logger 49 | - run: cargo run --bin static-creation-method 50 | - run: cargo run --bin adapter 51 | - run: cargo run --bin bridge 52 | - run: cargo run --bin composite 53 | - run: cargo run --bin decorator 54 | - run: cargo run --bin facade 55 | - run: cargo run --bin flyweight 56 | - run: cargo run --bin proxy 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "behavioral/chain-of-responsibility", 5 | "behavioral/command", 6 | "behavioral/iterator", 7 | "behavioral/mediator/mediator-rc-refcell", 8 | "behavioral/mediator/mediator-top-down", 9 | "behavioral/memento", 10 | "behavioral/observer", 11 | "behavioral/state", 12 | "behavioral/strategy", 13 | "behavioral/template-method", 14 | "behavioral/visitor", 15 | "creational/abstract-factory/app", 16 | "creational/abstract-factory/app-dyn", 17 | "creational/builder", 18 | "creational/factory-method/maze-game", 19 | "creational/factory-method/render-dialog", 20 | "creational/prototype", 21 | "creational/simple-factory", 22 | "creational/singleton/how-to-create", 23 | "creational/singleton/logger", 24 | "creational/static-creation-method", 25 | "structural/adapter", 26 | "structural/bridge", 27 | "structural/composite", 28 | "structural/decorator", 29 | "structural/facade", 30 | "structural/flyweight", 31 | "structural/proxy", 32 | ] 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Refactoring.Guru: Design Patterns in Rust 2 | Copyright (c) 2022 Alexander Fadeev 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Design Patterns in Rust](https://github.com/fadeevab/design-patterns-rust/workflows/Test%20All%20Patterns/badge.svg)](https://github.com/fadeevab/design-patterns-rust) 2 | 3 | # Design Patterns in Rust 4 | 5 | [](https://refactoring.guru) 6 | [](https://www.rust-lang.org/) 7 | 8 | This repository contains **Rust** 🦀 examples for **all 23 classic GoF design 9 | patterns**, and even a little more. 10 | 11 | All examples are designed to introduce _practical applicability_ in the 12 | **Rust** language. There are _conceptual_ and _real-world_ examples. 13 | In both cases, Rust idiomatic ways of code development and all the specifics 14 | are taken into account. 15 | 16 | The repository is developed to be a part of the 17 | [Refactoring.Guru](https://refactoring.guru/design-patterns) project. 18 | 19 | ## 🔧 Requirements 20 | 21 | These examples have been tested with a _stable_ `rustc 1.62` (2021 edition). 22 | 23 | All examples can be launched via the command line, using `cargo` as follows: 24 | 25 | ```bash 26 | cargo run --bin adapter 27 | ``` 28 | 29 | Each target name can be found in `Cargo.toml` of each example: 30 | 31 | ```toml 32 | [[bin]] 33 | name = "adapter" 34 | path = "main.rs" 35 | ``` 36 | 37 | Also, the examples contain a **README.md** with instructions and additional explanations. 38 | 39 | ## ✅ List of Examples 40 | 41 | ```bash 42 | cargo run --bin chain-of-responsibility 43 | cargo run --bin command 44 | cargo run --bin iterator 45 | cargo run --bin mediator-top-down 46 | cargo run --bin mediator-rc-refcell 47 | cargo run --bin memento 48 | cargo run --bin memento-serde 49 | cargo run --bin observer 50 | cargo run --bin state 51 | cargo run --bin strategy 52 | cargo run --bin strategy-func 53 | cargo run --bin template-method 54 | cargo run --bin visitor 55 | cargo run --bin abstract-factory 56 | cargo run --bin abstract-factory-dyn 57 | cargo run --bin builder 58 | cargo run --bin factory-method-maze-game 59 | cargo run --bin factory-method-render-dialog 60 | cargo run --bin prototype 61 | cargo run --bin simple-factory 62 | cargo run --bin singleton-local 63 | cargo run --bin singleton-lazy 64 | cargo run --bin singleton-mutex # Requires Rust 1.63 65 | cargo run --bin singleton-once 66 | cargo run --bin singleton-logger 67 | cargo run --bin static-creation-method 68 | cargo run --bin adapter 69 | cargo run --bin bridge 70 | cargo run --bin composite 71 | cargo run --bin decorator 72 | cargo run --bin facade 73 | cargo run --bin flyweight 74 | cargo run --bin proxy 75 | ``` 76 | 77 | Some examples have visual output. 78 | 79 | | Flyweight | State | Command | 80 | | --------- | ----- | ------- | 81 | | [](structural/flyweight) | [](behavioral/state) | [](behavioral/command) | 82 | 83 | ## 💡 Notes 84 | 85 | Interestingly, in Rust: 86 | 87 | 1. Almost all **structural** and **creational** patterns can be implemented 88 | using generics, hence, _static dispatch_. 89 | 2. Most **behavioral** patterns can NOT be implemented using static dispatch, 90 | instead, they can be implemented only via _dynamic dispatch_. 91 | 92 | A well-thought pattern classification fits the Rust language design perfectly 93 | as "behavior" is dynamic in nature and "structure" is static. 94 | 95 | Some patterns are really easy to implement in Rust, mostly 96 | _creational_ ones, e.g. 97 | [Prototype](creational/prototype), 98 | [Static Creation Method](creational/static-creation-method/). 99 | 100 | The [Mediator](behavioral/mediator) _behavioral_ pattern 101 | is the hardest to implement with Rust, considering Rust's specific ownership 102 | model with strict borrow checker rules. 103 | 104 | ## License 105 | 106 | This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. 107 | 108 | Creative Commons License 109 | 110 | ## Credits 111 | 112 | Authors: Alexander Fadeev ([@fadeevab](https://github.com/fadeevab)). 113 | -------------------------------------------------------------------------------- /behavioral/README.md: -------------------------------------------------------------------------------- 1 | # Behavioral Patterns 2 | 3 | _**Behavioral** design patterns are concerned with algorithms and the assignment of responsibilities between objects._ 4 | 5 | 👆 Each example contains its own README and instructions. -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "chain-of-responsibility" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "chain-of-responsibility" 8 | path = "main.rs" 9 | -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/README.md: -------------------------------------------------------------------------------- 1 | # Chain of Responsibility 2 | 3 | _**Chain of Responsibility** is behavioral design pattern that allows passing 4 | request along the chain of potential handlers until one of them handles request._ 5 | 6 | ## Conceptual Example 7 | 8 | The example demonstrates processing a patient through a chain of departments. 9 | The chain of responsibility is constructed as follows: 10 | 11 | ``` 12 | Patient -> Reception -> Doctor -> Medical -> Cashier 13 | ``` 14 | 15 | 💡 The chain is constructed using `Box` pointers, which means dynamic dispatch 16 | in runtime. **Why?** It seems quite difficult to narrow down implementation 17 | to a strict compile-time typing using generics: in order to construct a type 18 | of a full chain Rust needs full knowledge of the "next of the next" link in the 19 | chain. Thus, it ***would*** look like this: 20 | 21 | ```rust 22 | let mut reception = Reception::>>::new(doctor); // 😱 23 | ``` 24 | 25 | Instead, `Box` allows chaining in any combination: 26 | 27 | ```rust 28 | let mut reception = Reception::new(doctor); // 👍 29 | 30 | let mut reception = Reception::new(cashier); // 🕵️‍♀️ 31 | ``` 32 | 33 | ## How to Execute 34 | 35 | ```bash 36 | cargo run --bin chain-of-responsibility 37 | ``` 38 | 39 | ## Execution Result 40 | 41 | ``` 42 | Reception registering a patient John 43 | Doctor checking a patient John 44 | Medical giving medicine to a patient John 45 | Cashier getting money from a patient John 46 | 47 | The patient has been already handled: 48 | 49 | Patient registration is already done 50 | A doctor checkup is already done 51 | Medicine is already given to a patient 52 | Payment done 53 | ``` 54 | 55 | ## Reference 56 | 57 | [Chain of Responsibility in Go (Example)](https://refactoring.guru/design-patterns/chain-of-responsibility/go/example) is used as a reference for this Rust example. 58 | -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/department.rs: -------------------------------------------------------------------------------- 1 | mod cashier; 2 | mod doctor; 3 | mod medical; 4 | mod reception; 5 | 6 | pub use cashier::Cashier; 7 | pub use doctor::Doctor; 8 | pub use medical::Medical; 9 | pub use reception::Reception; 10 | 11 | use crate::patient::Patient; 12 | 13 | /// A single role of objects that make up a chain. 14 | /// A typical trait implementation must have `handle` and `next` methods, 15 | /// while `execute` is implemented by default and contains a proper chaining 16 | /// logic. 17 | pub trait Department { 18 | fn execute(&mut self, patient: &mut Patient) { 19 | self.handle(patient); 20 | 21 | if let Some(next) = &mut self.next() { 22 | next.execute(patient); 23 | } 24 | } 25 | 26 | fn handle(&mut self, patient: &mut Patient); 27 | fn next(&mut self) -> &mut Option>; 28 | } 29 | 30 | /// Helps to wrap an object into a boxed type. 31 | pub fn into_next(department: impl Department + Sized + 'static) -> Option> { 32 | Some(Box::new(department)) 33 | } 34 | -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/department/cashier.rs: -------------------------------------------------------------------------------- 1 | use super::{Department, Patient}; 2 | 3 | #[derive(Default)] 4 | pub struct Cashier { 5 | next: Option>, 6 | } 7 | 8 | impl Department for Cashier { 9 | fn handle(&mut self, patient: &mut Patient) { 10 | if patient.payment_done { 11 | println!("Payment done"); 12 | } else { 13 | println!("Cashier getting money from a patient {}", patient.name); 14 | patient.payment_done = true; 15 | } 16 | } 17 | 18 | fn next(&mut self) -> &mut Option> { 19 | &mut self.next 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/department/doctor.rs: -------------------------------------------------------------------------------- 1 | use super::{into_next, Department, Patient}; 2 | 3 | pub struct Doctor { 4 | next: Option>, 5 | } 6 | 7 | impl Doctor { 8 | pub fn new(next: impl Department + 'static) -> Self { 9 | Self { 10 | next: into_next(next), 11 | } 12 | } 13 | } 14 | 15 | impl Department for Doctor { 16 | fn handle(&mut self, patient: &mut Patient) { 17 | if patient.doctor_check_up_done { 18 | println!("A doctor checkup is already done"); 19 | } else { 20 | println!("Doctor checking a patient {}", patient.name); 21 | patient.doctor_check_up_done = true; 22 | } 23 | } 24 | 25 | fn next(&mut self) -> &mut Option> { 26 | &mut self.next 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/department/medical.rs: -------------------------------------------------------------------------------- 1 | use super::{into_next, Department, Patient}; 2 | 3 | pub struct Medical { 4 | next: Option>, 5 | } 6 | 7 | impl Medical { 8 | pub fn new(next: impl Department + 'static) -> Self { 9 | Self { 10 | next: into_next(next), 11 | } 12 | } 13 | } 14 | 15 | impl Department for Medical { 16 | fn handle(&mut self, patient: &mut Patient) { 17 | if patient.medicine_done { 18 | println!("Medicine is already given to a patient"); 19 | } else { 20 | println!("Medical giving medicine to a patient {}", patient.name); 21 | patient.medicine_done = true; 22 | } 23 | } 24 | 25 | fn next(&mut self) -> &mut Option> { 26 | &mut self.next 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/department/reception.rs: -------------------------------------------------------------------------------- 1 | use super::{into_next, Department, Patient}; 2 | 3 | #[derive(Default)] 4 | pub struct Reception { 5 | next: Option>, 6 | } 7 | 8 | impl Reception { 9 | pub fn new(next: impl Department + 'static) -> Self { 10 | Self { 11 | next: into_next(next), 12 | } 13 | } 14 | } 15 | 16 | impl Department for Reception { 17 | fn handle(&mut self, patient: &mut Patient) { 18 | if patient.registration_done { 19 | println!("Patient registration is already done"); 20 | } else { 21 | println!("Reception registering a patient {}", patient.name); 22 | patient.registration_done = true; 23 | } 24 | } 25 | 26 | fn next(&mut self) -> &mut Option> { 27 | &mut self.next 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/main.rs: -------------------------------------------------------------------------------- 1 | mod department; 2 | mod patient; 3 | 4 | use department::{Cashier, Department, Doctor, Medical, Reception}; 5 | use patient::Patient; 6 | 7 | fn main() { 8 | let cashier = Cashier::default(); 9 | let medical = Medical::new(cashier); 10 | let doctor = Doctor::new(medical); 11 | let mut reception = Reception::new(doctor); 12 | 13 | let mut patient = Patient { 14 | name: "John".into(), 15 | ..Patient::default() 16 | }; 17 | 18 | // Reception handles a patient passing him to the next link in the chain. 19 | // Reception -> Doctor -> Medical -> Cashier. 20 | reception.execute(&mut patient); 21 | 22 | println!("\nThe patient has been already handled:\n"); 23 | 24 | reception.execute(&mut patient); 25 | } 26 | -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/patient.rs: -------------------------------------------------------------------------------- 1 | #[derive(Default)] 2 | pub struct Patient { 3 | pub name: String, 4 | pub registration_done: bool, 5 | pub doctor_check_up_done: bool, 6 | pub medicine_done: bool, 7 | pub payment_done: bool, 8 | } 9 | -------------------------------------------------------------------------------- /behavioral/command/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "command" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "command" 8 | path = "main.rs" 9 | 10 | [dependencies] 11 | cursive = {version = "0.20", default-features = false, features = ["crossterm-backend"]} 12 | -------------------------------------------------------------------------------- /behavioral/command/README.md: -------------------------------------------------------------------------------- 1 | # Command 2 | 3 | _**Command** is behavioral design pattern that converts requests or 4 | simple operations into objects._ 5 | 6 | In 🦀 Rust, a command instance should _NOT hold a permanent reference to global 7 | context_, instead the latter should be passed _from top to down as a mutable 8 | parameter_ of the "`execute`" method: 9 | 10 | ```rust 11 | fn execute(&mut self, app: &mut cursive::Cursive) -> bool; 12 | ``` 13 | 14 | ## Text Editor: Commands and Undo 15 | 16 | How to launch: 17 | 18 | ```bash 19 | cargo run --bin command 20 | ``` 21 | 22 | Key points: 23 | 24 | - Each button runs a separate command. 25 | - Because a command is represented as an object, it can be pushed into a 26 | `history` array in order to be undone later. 27 | - TUI is created with `cursive` crate. 28 | 29 | ![Text Editor screenshot](res/editor.png) 30 | 31 | ## Reference 32 | 33 | This example is inspired by [Command in Java (Example)](https://refactoring.guru/design-patterns/command/java/example). 34 | -------------------------------------------------------------------------------- /behavioral/command/command.rs: -------------------------------------------------------------------------------- 1 | mod copy; 2 | mod cut; 3 | mod paste; 4 | 5 | pub use copy::CopyCommand; 6 | pub use cut::CutCommand; 7 | pub use paste::PasteCommand; 8 | 9 | /// Declares a method for executing (and undoing) a command. 10 | /// 11 | /// Each command receives an application context to access 12 | /// visual components (e.g. edit view) and a clipboard. 13 | pub trait Command { 14 | fn execute(&mut self, app: &mut cursive::Cursive) -> bool; 15 | fn undo(&mut self, app: &mut cursive::Cursive); 16 | } 17 | -------------------------------------------------------------------------------- /behavioral/command/command/copy.rs: -------------------------------------------------------------------------------- 1 | use cursive::{views::EditView, Cursive}; 2 | 3 | use super::Command; 4 | use crate::AppContext; 5 | 6 | #[derive(Default)] 7 | pub struct CopyCommand; 8 | 9 | impl Command for CopyCommand { 10 | fn execute(&mut self, app: &mut Cursive) -> bool { 11 | let editor = app.find_name::("Editor").unwrap(); 12 | let mut context = app.take_user_data::().unwrap(); 13 | 14 | context.clipboard = editor.get_content().to_string(); 15 | 16 | app.set_user_data(context); 17 | false 18 | } 19 | 20 | fn undo(&mut self, _: &mut Cursive) {} 21 | } 22 | -------------------------------------------------------------------------------- /behavioral/command/command/cut.rs: -------------------------------------------------------------------------------- 1 | use cursive::{views::EditView, Cursive}; 2 | 3 | use super::Command; 4 | use crate::AppContext; 5 | 6 | #[derive(Default)] 7 | pub struct CutCommand { 8 | backup: String, 9 | } 10 | 11 | impl Command for CutCommand { 12 | fn execute(&mut self, app: &mut Cursive) -> bool { 13 | let mut editor = app.find_name::("Editor").unwrap(); 14 | 15 | app.with_user_data(|context: &mut AppContext| { 16 | self.backup = editor.get_content().to_string(); 17 | context.clipboard = self.backup.clone(); 18 | editor.set_content("".to_string()); 19 | }); 20 | 21 | true 22 | } 23 | 24 | fn undo(&mut self, app: &mut Cursive) { 25 | let mut editor = app.find_name::("Editor").unwrap(); 26 | editor.set_content(&self.backup); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /behavioral/command/command/paste.rs: -------------------------------------------------------------------------------- 1 | use cursive::{views::EditView, Cursive}; 2 | 3 | use super::Command; 4 | use crate::AppContext; 5 | 6 | #[derive(Default)] 7 | pub struct PasteCommand { 8 | backup: String, 9 | } 10 | 11 | impl Command for PasteCommand { 12 | fn execute(&mut self, app: &mut Cursive) -> bool { 13 | let mut editor = app.find_name::("Editor").unwrap(); 14 | 15 | app.with_user_data(|context: &mut AppContext| { 16 | self.backup = editor.get_content().to_string(); 17 | editor.set_content(context.clipboard.clone()); 18 | }); 19 | 20 | true 21 | } 22 | 23 | fn undo(&mut self, app: &mut Cursive) { 24 | let mut editor = app.find_name::("Editor").unwrap(); 25 | editor.set_content(&self.backup); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /behavioral/command/main.rs: -------------------------------------------------------------------------------- 1 | mod command; 2 | 3 | use cursive::{ 4 | traits::Nameable, 5 | views::{Dialog, EditView}, 6 | Cursive, 7 | }; 8 | 9 | use command::{Command, CopyCommand, CutCommand, PasteCommand}; 10 | 11 | /// An application context to be passed into visual component callbacks. 12 | /// It contains a clipboard and a history of commands to be undone. 13 | #[derive(Default)] 14 | struct AppContext { 15 | clipboard: String, 16 | history: Vec>, 17 | } 18 | 19 | fn main() { 20 | let mut app = cursive::default(); 21 | 22 | app.set_user_data(AppContext::default()); 23 | app.add_layer( 24 | Dialog::around(EditView::default().with_name("Editor")) 25 | .title("Type and use buttons") 26 | .button("Copy", |s| execute(s, CopyCommand)) 27 | .button("Cut", |s| execute(s, CutCommand::default())) 28 | .button("Paste", |s| execute(s, PasteCommand::default())) 29 | .button("Undo", undo) 30 | .button("Quit", |s| s.quit()), 31 | ); 32 | 33 | app.run(); 34 | } 35 | 36 | /// Executes a command and then pushes it to a history array. 37 | fn execute(app: &mut Cursive, mut command: impl Command + 'static) { 38 | if command.execute(app) { 39 | app.with_user_data(|context: &mut AppContext| { 40 | context.history.push(Box::new(command)); 41 | }); 42 | } 43 | } 44 | 45 | /// Pops the last command and executes an undo action. 46 | fn undo(app: &mut Cursive) { 47 | let mut context = app.take_user_data::().unwrap(); 48 | if let Some(mut command) = context.history.pop() { 49 | command.undo(app) 50 | } 51 | app.set_user_data(context); 52 | } 53 | -------------------------------------------------------------------------------- /behavioral/command/res/editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RefactoringGuru/design-patterns-rust/0ac81c6e1446ddb333cd4ddef10403d31f7ad23b/behavioral/command/res/editor.png -------------------------------------------------------------------------------- /behavioral/iterator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "iterator" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "iterator" 8 | path = "main.rs" 9 | -------------------------------------------------------------------------------- /behavioral/iterator/README.md: -------------------------------------------------------------------------------- 1 | # Iterator 2 | 3 | _**Iterator** is a behavioral design pattern that allows sequential traversal 4 | through a complex data structure without exposing its internal details._ 5 | 6 | ## Standard Iterator 7 | 8 | Iterators are heavily used in idiomatic 🦀 Rust code. The first example is 9 | how to use iterators over a standard array collection. 10 | 11 | ```rust 12 | let array = &[1, 2, 3]; 13 | let iterator = array.iter(); 14 | 15 | // Traversal over each element of the vector. 16 | iterator.for_each(|e| print!("{}, ", e)); 17 | ``` 18 | 19 | ## Custom Iterator 20 | 21 | In Rust, the recommended way to define your _custom_ iterator is to use a 22 | standard `Iterator` trait. The example doesn't contain a synthetic iterator 23 | interface, because it is really recommended to use the idiomatic Rust way. 24 | 25 | ```rust 26 | let users = UserCollection::new(); 27 | let mut iterator = users.iter(); 28 | 29 | iterator.next(); 30 | ``` 31 | 32 | A `next` method is the only `Iterator` trait method which is mandatory to be 33 | implemented. It makes accessible a huge range of standard methods, 34 | e.g. `fold`, `map`, `for_each`. 35 | 36 | ```rust 37 | impl Iterator for UserIterator<'_> { 38 | fn next(&mut self) -> Option; 39 | } 40 | ``` 41 | 42 | ## How to Run 43 | 44 | ```bash 45 | cargo run --bin iterator 46 | ``` 47 | 48 | ## Execution Result 49 | 50 | ``` 51 | Iterators are widely used in the standard library: 1 2 3 52 | 53 | Let's test our own iterator. 54 | 55 | 1nd element: Some("Alice") 56 | 2nd element: Some("Bob") 57 | 3rd element: Some("Carl") 58 | 4th element: None 59 | 60 | 61 | All elements in user collection: Alice Bob Carl 62 | ``` 63 | -------------------------------------------------------------------------------- /behavioral/iterator/main.rs: -------------------------------------------------------------------------------- 1 | use crate::users::UserCollection; 2 | 3 | mod users; 4 | 5 | fn main() { 6 | print!("Iterators are widely used in the standard library: "); 7 | 8 | let array = &[1, 2, 3]; 9 | let iterator = array.iter(); 10 | 11 | // Traversal over each element of the array. 12 | iterator.for_each(|e| print!("{} ", e)); 13 | 14 | println!("\n\nLet's test our own iterator.\n"); 15 | 16 | let users = UserCollection::new(); 17 | let mut iterator = users.iter(); 18 | 19 | println!("1nd element: {:?}", iterator.next()); 20 | println!("2nd element: {:?}", iterator.next()); 21 | println!("3rd element: {:?}", iterator.next()); 22 | println!("4th element: {:?}", iterator.next()); 23 | 24 | print!("\nAll elements in user collection: "); 25 | users.iter().for_each(|e| print!("{} ", e)); 26 | 27 | println!(); 28 | } 29 | -------------------------------------------------------------------------------- /behavioral/iterator/users.rs: -------------------------------------------------------------------------------- 1 | pub struct UserCollection { 2 | users: [&'static str; 3], 3 | } 4 | 5 | /// A custom collection contains an arbitrary user array under the hood. 6 | impl UserCollection { 7 | /// Returns a custom user collection. 8 | pub fn new() -> Self { 9 | Self { 10 | users: ["Alice", "Bob", "Carl"], 11 | } 12 | } 13 | 14 | /// Returns an iterator over a user collection. 15 | /// 16 | /// The method name may be different, however, `iter` is used as a de facto 17 | /// standard in a Rust naming convention. 18 | pub fn iter(&self) -> UserIterator { 19 | UserIterator { 20 | index: 0, 21 | user_collection: self, 22 | } 23 | } 24 | } 25 | 26 | /// UserIterator allows sequential traversal through a complex user collection 27 | /// without exposing its internal details. 28 | pub struct UserIterator<'a> { 29 | index: usize, 30 | user_collection: &'a UserCollection, 31 | } 32 | 33 | /// `Iterator` is a standard interface for dealing with iterators 34 | /// from the Rust standard library. 35 | impl Iterator for UserIterator<'_> { 36 | type Item = &'static str; 37 | 38 | /// A `next` method is the only `Iterator` trait method which is mandatory to be 39 | /// implemented. It makes accessible a huge range of standard methods, 40 | /// e.g. `fold`, `map`, `for_each`. 41 | fn next(&mut self) -> Option { 42 | if self.index < self.user_collection.users.len() { 43 | let user = Some(self.user_collection.users[self.index]); 44 | self.index += 1; 45 | return user; 46 | } 47 | 48 | None 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /behavioral/mediator/README.md: -------------------------------------------------------------------------------- 1 | # Mediator 2 | 3 | _**Mediator** restricts direct communications between the objects and forces them 4 | to collaborate only via a mediator object_. It also stands for a Controller in the MVC (Model-View-Controller) pattern. 5 | 6 | ## How to Run 7 | 8 | ```bash 9 | cargo run --bin mediator-top-down 10 | cargo run --bin mediator-rc-refcell 11 | ``` 12 | 13 | ## Execution Result 14 | 15 | Output of the `mediator-top-down`. 16 | 17 | ``` 18 | Passenger train Train 1: Arrived 19 | Freight train Train 2: Arrival blocked, waiting 20 | Passenger train Train 1: Leaving 21 | Freight train Train 2: Arrived 22 | Freight train Train 2: Leaving 23 | 'Train 3' is not on the station! 24 | ``` 25 | 26 | ## Problem 27 | 28 | _*Mediator* is a challenging pattern to be implemented in *Rust*._ 29 | 30 | A typical Mediator implementation in other languages is a classic anti-pattern 31 | in Rust: many objects hold mutable cross-references on each other, trying to 32 | mutate each other, which is a deadly sin in Rust - the compiler won't pass your 33 | first naive implementation unless it's oversimplified. 34 | 35 | By definition, [Mediator][1] restricts direct communications between the objects 36 | and forces them to collaborate only via a mediator object. It also stands for 37 | a Controller in the MVC pattern. Let's see the nice diagrams from 38 | https://refactoring.guru: 39 | 40 | | Problem | Solution | 41 | | ---------------------------- | ----------------------------- | 42 | | ![image](images/problem.png) | ![image](images/solution.png) | 43 | 44 | A common implementation in object-oriented languages looks like the following 45 | pseudo-code: 46 | 47 | ```java 48 | Controller controller = new Controller(); 49 | 50 | // Every component has a link to a mediator (controller). 51 | component1.setController(controller); 52 | component2.setController(controller); 53 | component3.setController(controller); 54 | 55 | // A mediator has a link to every object. 56 | controller.add(component1); 57 | controller.add(component2); 58 | controller.add(component2); 59 | ``` 60 | 61 | Now, let's read this in **Rust** terms: _"**mutable** structures have 62 | **mutable** references to a **shared mutable** object (mediator) which in turn 63 | has mutable references back to those mutable structures"_. 64 | 65 | Basically, you can start to imagine the unfair battle against the Rust compiler 66 | and its borrow checker. It seems like a solution introduces more problems: 67 | 68 | ![image](images/mediator-mut-problem.png) 69 | 70 | 1. Imagine that the control flow starts at point 1 (Checkbox) where the 1st 71 | **mutable** borrow happens. 72 | 2. The mediator (Dialog) interacts with another object at point 2 (TextField). 73 | 3. The TextField notifies the Dialog back about finishing a job and that leads 74 | to a **mutable** action at point 3... Bang! 75 | 76 | The second mutable borrow breaks the compilation with an error 77 | (the first borrow was on the point 1). 78 | 79 | ## Cross-Referencing with `Rc>` 80 | 81 | ```bash 82 | cargo run --bin mediator-rc-refcell 83 | ``` 84 | 85 | `Rc>` hides objects from compiler eyes inside of an opaque smart pointer. 86 | In this case, borrow checks move into the runtime that means panicking in case of 87 | borrow rules violation. 88 | 89 | There is an example of a [Station Manager example in Go][4]. Trying to make it 90 | with Rust leads to mimicking a typical OOP through reference counting and 91 | borrow checking with mutability in runtime (which has quite unpredictable 92 | behavior in runtime with panics here and there). 93 | 94 | Key points: 95 | 96 | 1. All trait methods are **read-only**: immutable `self` and immutable parameters. 97 | 2. `Rc`, `RefCell` are extensively used under the hood to take responsibility 98 | for the mutable borrowing from compiler to runtime. Invalid implementation 99 | will lead to panic in runtime. 100 | 101 | ## Top-Down Ownership 102 | 103 | ```bash 104 | cargo run --bin mediator-top-down 105 | ``` 106 | 107 | ☝ The key point is thinking in terms of OWNERSHIP. 108 | 109 | ![Ownership](images/mediator-rust-approach.jpg) 110 | 111 | 1. A mediator takes ownership of all components. 112 | 2. A component doesn't preserve a reference to a mediator. Instead, it gets the 113 | reference via a method call. 114 | 115 | ```rust 116 | // A train gets a mediator object by reference. 117 | pub trait Train { 118 | fn name(&self) -> &String; 119 | fn arrive(&mut self, mediator: &mut dyn Mediator); 120 | fn depart(&mut self, mediator: &mut dyn Mediator); 121 | } 122 | 123 | // Mediator has notification methods. 124 | pub trait Mediator { 125 | fn notify_about_arrival(&mut self, train_name: &str) -> bool; 126 | fn notify_about_departure(&mut self, train_name: &str); 127 | } 128 | ``` 129 | 130 | 3. Control flow starts from `fn main()` where the mediator receives external 131 | events/commands. 132 | 4. `Mediator` trait for the interaction between components 133 | (`notify_about_arrival`, `notify_about_departure`) is not the same as its 134 | external API for receiving external events (`accept`, `depart` commands from 135 | the main loop). 136 | 137 | ```rust 138 | let train1 = PassengerTrain::new("Train 1"); 139 | let train2 = FreightTrain::new("Train 2"); 140 | 141 | // Station has `accept` and `depart` methods, 142 | // but it also implements `Mediator`. 143 | let mut station = TrainStation::default(); 144 | 145 | // Station is taking ownership of the trains. 146 | station.accept(train1); 147 | station.accept(train2); 148 | 149 | // `train1` and `train2` have been moved inside, 150 | // but we can use train names to depart them. 151 | station.depart("Train 1"); 152 | station.depart("Train 2"); 153 | station.depart("Train 3"); 154 | ``` 155 | 156 | A few changes to the direct approach leads to a safe mutability being checked 157 | at compilation time. 158 | 159 | 👉 A real-world example of such approach: [Cursive (TUI)][5]. 160 | 161 | [1]: https://refactoring.guru/design-patterns/mediator 162 | [2]: https://github.com/rust-unofficial/patterns/issues/233 163 | [3]: https://chercher.tech/rust/mediator-design-pattern-rust 164 | [4]: https://refactoring.guru/design-patterns/mediator/go/example 165 | [5]: https://crates.io/crates/cursive 166 | -------------------------------------------------------------------------------- /behavioral/mediator/images/mediator-mut-problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RefactoringGuru/design-patterns-rust/0ac81c6e1446ddb333cd4ddef10403d31f7ad23b/behavioral/mediator/images/mediator-mut-problem.png -------------------------------------------------------------------------------- /behavioral/mediator/images/mediator-rust-approach.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RefactoringGuru/design-patterns-rust/0ac81c6e1446ddb333cd4ddef10403d31f7ad23b/behavioral/mediator/images/mediator-rust-approach.jpg -------------------------------------------------------------------------------- /behavioral/mediator/images/problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RefactoringGuru/design-patterns-rust/0ac81c6e1446ddb333cd4ddef10403d31f7ad23b/behavioral/mediator/images/problem.png -------------------------------------------------------------------------------- /behavioral/mediator/images/solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RefactoringGuru/design-patterns-rust/0ac81c6e1446ddb333cd4ddef10403d31f7ad23b/behavioral/mediator/images/solution.png -------------------------------------------------------------------------------- /behavioral/mediator/mediator-rc-refcell/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "mediator-rc-refcell" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "mediator-rc-refcell" 8 | path = "main.rs" 9 | -------------------------------------------------------------------------------- /behavioral/mediator/mediator-rc-refcell/README.md: -------------------------------------------------------------------------------- 1 | # Mediator with `Rc>` 2 | 3 | ## How To Run 4 | 5 | ```bash 6 | cargo run --bin mediator-rc-refcell 7 | ``` 8 | 9 | ## Mimicking a Typical OOP 10 | 11 | `Rc>` hides objects from compiler eyes inside of an opaque smart pointer. 12 | In this case, borrow checks move into the runtime that means panicking in case of 13 | borrow rules violation. 14 | 15 | There is an example of a [Station Manager example in Go][4]. Trying to make it 16 | with Rust leads to mimicking a typical OOP through reference counting and 17 | borrow checking with mutability in runtime (which has quite unpredictable 18 | behavior in runtime with panics here and there). 19 | 20 | Key points: 21 | 22 | 1. All methods are read-only: immutable `self` and parameters. 23 | 2. `Rc`, `RefCell` are extensively used under the hood to take responsibility for the mutable borrowing from compilation time to runtime. Invalid implementation will lead to panic in runtime. 24 | 25 | See the full article: [README.md](../README.md). 26 | 27 | [4]: https://refactoring.guru/design-patterns/mediator/go/example -------------------------------------------------------------------------------- /behavioral/mediator/mediator-rc-refcell/main.rs: -------------------------------------------------------------------------------- 1 | mod train_station; 2 | mod trains; 3 | 4 | use std::{cell::RefCell, rc::Rc}; 5 | 6 | use train_station::StationManager; 7 | use trains::{FreightTrain, PassengerTrain, Train}; 8 | 9 | fn main() { 10 | let station = Rc::new(RefCell::new(StationManager::default())); 11 | 12 | let train1 = Rc::new(PassengerTrain::new("Train 1".into(), station.clone())); 13 | let train2 = Rc::new(FreightTrain::new("Train 2".into(), station.clone())); 14 | 15 | { 16 | let mut station = station.borrow_mut(); 17 | station.register(train1.clone()); 18 | station.register(train2.clone()); 19 | } 20 | 21 | train1.arrive(); 22 | train2.arrive(); 23 | train1.depart(); 24 | train2.depart(); 25 | } 26 | -------------------------------------------------------------------------------- /behavioral/mediator/mediator-rc-refcell/train_station.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cell::RefCell, 3 | collections::{HashMap, VecDeque}, 4 | rc::Rc, 5 | }; 6 | 7 | use crate::trains::Train; 8 | 9 | pub trait Mediator { 10 | fn notify_about_arrival(&self, train: &dyn Train) -> bool; 11 | fn notify_about_departure(&self, train: &dyn Train); 12 | } 13 | 14 | #[derive(Default)] 15 | pub struct StationManager { 16 | trains: HashMap>, 17 | train_queue: RefCell>, 18 | train_on_platform: RefCell>, 19 | } 20 | 21 | impl StationManager { 22 | pub fn register(&mut self, train: Rc) { 23 | self.trains.insert(train.name().clone(), train); 24 | } 25 | } 26 | 27 | impl Mediator for StationManager { 28 | fn notify_about_arrival(&self, train: &dyn Train) -> bool { 29 | let train_name = train.name().clone(); 30 | 31 | self.trains.get(&train_name).expect("A train should exist"); 32 | 33 | if self.train_on_platform.borrow().is_some() { 34 | self.train_queue.borrow_mut().push_back(train_name); 35 | return false; 36 | } 37 | 38 | self.train_on_platform.replace(Some(train_name)); 39 | true 40 | } 41 | 42 | fn notify_about_departure(&self, train: &dyn Train) { 43 | if Some(train.name().clone()) != self.train_on_platform.replace(None) { 44 | return; 45 | } 46 | 47 | let next_train = self.train_queue.borrow_mut().pop_front(); 48 | 49 | if let Some(next_train_name) = next_train { 50 | let next_train = self.trains.get(&next_train_name).unwrap(); 51 | next_train.arrive(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /behavioral/mediator/mediator-rc-refcell/trains/freight_train.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, rc::Rc}; 2 | 3 | use super::Train; 4 | use crate::train_station::Mediator; 5 | 6 | pub struct FreightTrain { 7 | name: String, 8 | mediator: Rc>, 9 | } 10 | 11 | impl FreightTrain { 12 | pub fn new(name: String, mediator: Rc>) -> Self { 13 | Self { name, mediator } 14 | } 15 | } 16 | 17 | impl Train for FreightTrain { 18 | fn name(&self) -> &String { 19 | &self.name 20 | } 21 | 22 | fn arrive(&self) { 23 | if !self.mediator.borrow().notify_about_arrival(self) { 24 | println!("Freight train {}: Arrival blocked, waiting", self.name); 25 | return; 26 | } 27 | 28 | println!("Freight train {}: Arrived", self.name); 29 | } 30 | 31 | fn depart(&self) { 32 | println!("Freight train {}: Leaving", self.name); 33 | self.mediator.borrow().notify_about_departure(self); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /behavioral/mediator/mediator-rc-refcell/trains/mod.rs: -------------------------------------------------------------------------------- 1 | mod freight_train; 2 | mod passenger_train; 3 | 4 | pub use freight_train::FreightTrain; 5 | pub use passenger_train::PassengerTrain; 6 | 7 | pub trait Train { 8 | fn name(&self) -> &String; 9 | fn arrive(&self); 10 | fn depart(&self); 11 | } 12 | -------------------------------------------------------------------------------- /behavioral/mediator/mediator-rc-refcell/trains/passenger_train.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, rc::Rc}; 2 | 3 | use super::Train; 4 | use crate::train_station::Mediator; 5 | 6 | pub struct PassengerTrain { 7 | name: String, 8 | mediator: Rc>, 9 | } 10 | 11 | impl PassengerTrain { 12 | pub fn new(name: String, mediator: Rc>) -> Self { 13 | Self { name, mediator } 14 | } 15 | } 16 | 17 | impl Train for PassengerTrain { 18 | fn name(&self) -> &String { 19 | &self.name 20 | } 21 | 22 | fn arrive(&self) { 23 | if !self.mediator.borrow().notify_about_arrival(self) { 24 | println!("Passenger train {}: Arrival blocked, waiting", self.name); 25 | return; 26 | } 27 | 28 | println!("Passenger train {}: Arrived", self.name); 29 | } 30 | 31 | fn depart(&self) { 32 | println!("Passenger train {}: Leaving", self.name); 33 | self.mediator.borrow().notify_about_departure(self); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /behavioral/mediator/mediator-top-down/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "mediator-top-down" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "mediator-top-down" 8 | path = "main.rs" 9 | -------------------------------------------------------------------------------- /behavioral/mediator/mediator-top-down/README.md: -------------------------------------------------------------------------------- 1 | # Mediator with Top-Down Ownership 2 | 3 | Top-Down Ownership approach allows to apply Mediator in Rust as it is 4 | a suitable for Rust's ownership model with strict borrow checker rules. 5 | 6 | ## How To Run 7 | 8 | ```bash 9 | cargo run --bin mediator-top-down 10 | ``` 11 | 12 | ## Execution Result 13 | 14 | ``` 15 | Passenger train Train 1: Arrived 16 | Freight train Train 2: Arrival blocked, waiting 17 | Passenger train Train 1: Leaving 18 | Freight train Train 2: Arrived 19 | Freight train Train 2: Leaving 20 | 'Train 3' is not on the station! 21 | ``` 22 | 23 | ## Top-Down Ownership 24 | 25 | The key point is thinking in terms of OWNERSHIP. 26 | 27 | 1. A mediator takes ownership of all components. 28 | 2. A component doesn't preserve a reference to a mediator. Instead, it gets the reference via a method call. 29 | 30 | ```rust 31 | // A train gets a mediator object by reference. 32 | pub trait Train { 33 | fn name(&self) -> &String; 34 | fn arrive(&mut self, mediator: &mut dyn Mediator); 35 | fn depart(&mut self, mediator: &mut dyn Mediator); 36 | } 37 | 38 | // Mediator has notification methods. 39 | pub trait Mediator { 40 | fn notify_about_arrival(&mut self, train_name: &str) -> bool; 41 | fn notify_about_departure(&mut self, train_name: &str); 42 | } 43 | ``` 44 | 45 | 3. Control flow starts from `fn main()` where the mediator receives external events/commands. 46 | 4. `Mediator` trait for the interaction between components (`notify_about_arrival`, `notify_about_departure`) is not the same as its external API for receiving external events (`accept`, `depart` commands from the main loop). 47 | 48 | ```rust 49 | let train1 = PassengerTrain::new("Train 1"); 50 | let train2 = FreightTrain::new("Train 2"); 51 | 52 | // Station has `accept` and `depart` methods, 53 | // but it also implements `Mediator`. 54 | let mut station = TrainStation::default(); 55 | 56 | // Station is taking ownership of the trains. 57 | station.accept(train1); 58 | station.accept(train2); 59 | 60 | // `train1` and `train2` have been moved inside, 61 | // but we can use train names to depart them. 62 | station.depart("Train 1"); 63 | station.depart("Train 2"); 64 | station.depart("Train 3"); 65 | ``` 66 | 67 | ![Top-Down Ownership](https://github.com/fadeevab/mediator-pattern-rust/raw/main/images/mediator-rust-approach.jpg) 68 | 69 | ## References 70 | 71 | 1. [Mediator Pattern in Rust](https://github.com/fadeevab/mediator-pattern-rust) 72 | 2. [Mediator in Go (Example)](https://refactoring.guru/design-patterns/mediator/go/example) 73 | -------------------------------------------------------------------------------- /behavioral/mediator/mediator-top-down/main.rs: -------------------------------------------------------------------------------- 1 | mod train_station; 2 | mod trains; 3 | 4 | use train_station::TrainStation; 5 | use trains::{FreightTrain, PassengerTrain}; 6 | 7 | fn main() { 8 | let train1 = PassengerTrain::new("Train 1"); 9 | let train2 = FreightTrain::new("Train 2"); 10 | 11 | // Station has `accept` and `depart` methods, 12 | // but it also implements `Mediator`. 13 | let mut station = TrainStation::default(); 14 | 15 | // Station is taking ownership of the trains. 16 | station.accept(train1); 17 | station.accept(train2); 18 | 19 | // `train1` and `train2` have been moved inside, 20 | // but we can use train names to depart them. 21 | station.depart("Train 1"); 22 | station.depart("Train 2"); 23 | station.depart("Train 3"); 24 | } 25 | -------------------------------------------------------------------------------- /behavioral/mediator/mediator-top-down/train_station.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashMap, VecDeque}; 2 | 3 | use crate::trains::Train; 4 | 5 | // Mediator has notification methods. 6 | pub trait Mediator { 7 | fn notify_about_arrival(&mut self, train_name: &str) -> bool; 8 | fn notify_about_departure(&mut self, train_name: &str); 9 | } 10 | 11 | #[derive(Default)] 12 | pub struct TrainStation { 13 | trains: HashMap>, 14 | train_queue: VecDeque, 15 | train_on_platform: Option, 16 | } 17 | 18 | impl Mediator for TrainStation { 19 | fn notify_about_arrival(&mut self, train_name: &str) -> bool { 20 | if self.train_on_platform.is_some() { 21 | self.train_queue.push_back(train_name.into()); 22 | false 23 | } else { 24 | self.train_on_platform.replace(train_name.into()); 25 | true 26 | } 27 | } 28 | 29 | fn notify_about_departure(&mut self, train_name: &str) { 30 | if Some(train_name.into()) == self.train_on_platform { 31 | self.train_on_platform = None; 32 | 33 | if let Some(next_train_name) = self.train_queue.pop_front() { 34 | let mut next_train = self.trains.remove(&next_train_name).unwrap(); 35 | next_train.arrive(self); 36 | self.trains.insert(next_train_name.clone(), next_train); 37 | 38 | self.train_on_platform = Some(next_train_name); 39 | } 40 | } 41 | } 42 | } 43 | 44 | impl TrainStation { 45 | pub fn accept(&mut self, mut train: impl Train + 'static) { 46 | if self.trains.contains_key(train.name()) { 47 | println!("{} has already arrived", train.name()); 48 | return; 49 | } 50 | 51 | train.arrive(self); 52 | self.trains.insert(train.name().clone(), Box::new(train)); 53 | } 54 | 55 | pub fn depart(&mut self, name: &'static str) { 56 | let train = self.trains.remove(name); 57 | if let Some(mut train) = train { 58 | train.depart(self); 59 | } else { 60 | println!("'{}' is not on the station!", name); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /behavioral/mediator/mediator-top-down/trains/freight_train.rs: -------------------------------------------------------------------------------- 1 | use super::Train; 2 | use crate::train_station::Mediator; 3 | 4 | pub struct FreightTrain { 5 | name: String, 6 | } 7 | 8 | impl FreightTrain { 9 | pub fn new(name: &'static str) -> Self { 10 | Self { name: name.into() } 11 | } 12 | } 13 | 14 | impl Train for FreightTrain { 15 | fn name(&self) -> &String { 16 | &self.name 17 | } 18 | 19 | fn arrive(&mut self, mediator: &mut dyn Mediator) { 20 | if !mediator.notify_about_arrival(&self.name) { 21 | println!("Freight train {}: Arrival blocked, waiting", self.name); 22 | return; 23 | } 24 | 25 | println!("Freight train {}: Arrived", self.name); 26 | } 27 | 28 | fn depart(&mut self, mediator: &mut dyn Mediator) { 29 | println!("Freight train {}: Leaving", self.name); 30 | mediator.notify_about_departure(&self.name); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /behavioral/mediator/mediator-top-down/trains/mod.rs: -------------------------------------------------------------------------------- 1 | mod freight_train; 2 | mod passenger_train; 3 | 4 | pub use freight_train::FreightTrain; 5 | pub use passenger_train::PassengerTrain; 6 | 7 | use crate::train_station::Mediator; 8 | 9 | // A train gets a mediator object by reference. 10 | pub trait Train { 11 | fn name(&self) -> &String; 12 | fn arrive(&mut self, mediator: &mut dyn Mediator); 13 | fn depart(&mut self, mediator: &mut dyn Mediator); 14 | } 15 | -------------------------------------------------------------------------------- /behavioral/mediator/mediator-top-down/trains/passenger_train.rs: -------------------------------------------------------------------------------- 1 | use super::Train; 2 | use crate::train_station::Mediator; 3 | 4 | pub struct PassengerTrain { 5 | name: String, 6 | } 7 | 8 | impl PassengerTrain { 9 | pub fn new(name: &'static str) -> Self { 10 | Self { name: name.into() } 11 | } 12 | } 13 | 14 | impl Train for PassengerTrain { 15 | fn name(&self) -> &String { 16 | &self.name 17 | } 18 | 19 | fn arrive(&mut self, mediator: &mut dyn Mediator) { 20 | if !mediator.notify_about_arrival(&self.name) { 21 | println!("Passenger train {}: Arrival blocked, waiting", self.name); 22 | return; 23 | } 24 | 25 | println!("Passenger train {}: Arrived", self.name); 26 | } 27 | 28 | fn depart(&mut self, mediator: &mut dyn Mediator) { 29 | println!("Passenger train {}: Leaving", self.name); 30 | mediator.notify_about_departure(&self.name); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /behavioral/memento/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "memento" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "memento" 8 | path = "conceptual.rs" 9 | 10 | [[bin]] 11 | name = "memento-serde" 12 | path = "serde.rs" 13 | 14 | [dependencies] 15 | serde = {version = "1.0", features = ["derive"]} 16 | serde_json = "1.0" 17 | -------------------------------------------------------------------------------- /behavioral/memento/README.md: -------------------------------------------------------------------------------- 1 | # Memento 2 | 3 | _**Memento** allows making snapshots of an object’s state and restoring it in future._ 4 | 5 | ## `conceptual.rs` 6 | 7 | This is a conceptual example of Memento pattern. Keep in mind, that saving and restoring objects can be done via `serde` framework, see an example below. 8 | 9 | ### How to Run 10 | 11 | ```bash 12 | cargo run --bin memento 13 | ``` 14 | 15 | ### Output 16 | 17 | ``` 18 | Originator backup: '1' 19 | Originator backup: '2' 20 | Restored to state: 2 21 | Restored to state: 1 22 | ``` 23 | 24 | ## `serde.rs` 25 | 26 | A common way to make a structure serializable is to derive `Serialize` and 27 | `Deserialize` traits from `serde` crate. Then an object of serializable type 28 | can be converted to many different formats, e.g. JSON with `serde_json` crate. 29 | 30 | ```rust 31 | use serde::{Deserialize, Serialize}; 32 | 33 | #[derive(Serialize, Deserialize)] 34 | struct Originator { 35 | state: u32, 36 | } 37 | ``` 38 | 39 | ### How to Run 40 | 41 | ```bash 42 | cargo run --bin memento-serde 43 | ``` 44 | 45 | ### Output 46 | 47 | ``` 48 | {"state":1} 49 | {"state":2} 50 | Restored to state: 2 51 | Restored to state: 1 52 | ``` 53 | -------------------------------------------------------------------------------- /behavioral/memento/conceptual.rs: -------------------------------------------------------------------------------- 1 | trait Memento { 2 | fn restore(self) -> T; 3 | fn print(&self); 4 | } 5 | 6 | struct Originator { 7 | state: u32, 8 | } 9 | 10 | impl Originator { 11 | pub fn save(&self) -> OriginatorBackup { 12 | OriginatorBackup { 13 | state: self.state.to_string(), 14 | } 15 | } 16 | } 17 | 18 | struct OriginatorBackup { 19 | state: String, 20 | } 21 | 22 | impl Memento for OriginatorBackup { 23 | fn restore(self) -> Originator { 24 | Originator { 25 | state: self.state.parse().unwrap(), 26 | } 27 | } 28 | 29 | fn print(&self) { 30 | println!("Originator backup: '{}'", self.state); 31 | } 32 | } 33 | 34 | fn main() { 35 | let mut history = Vec::::new(); 36 | 37 | let mut originator = Originator { state: 0 }; 38 | 39 | originator.state = 1; 40 | history.push(originator.save()); 41 | 42 | originator.state = 2; 43 | history.push(originator.save()); 44 | 45 | for moment in history.iter() { 46 | moment.print(); 47 | } 48 | 49 | let originator = history.pop().unwrap().restore(); 50 | println!("Restored to state: {}", originator.state); 51 | 52 | let originator = history.pop().unwrap().restore(); 53 | println!("Restored to state: {}", originator.state); 54 | } 55 | -------------------------------------------------------------------------------- /behavioral/memento/serde.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// An object to be stored. It derives a default 4 | /// `Serialize` and `Deserialize` trait implementation, which 5 | /// allows to convert it into many different formats (e.g. JSON). 6 | #[derive(Serialize, Deserialize)] 7 | struct Originator { 8 | state: u32, 9 | } 10 | 11 | impl Originator { 12 | /// Serializes an originator into a string of JSON format. 13 | pub fn save(&self) -> String { 14 | serde_json::to_string(self).unwrap() 15 | } 16 | 17 | /// Deserializes an originator into a string of JSON format. 18 | pub fn restore(json: &str) -> Self { 19 | serde_json::from_str(json).unwrap() 20 | } 21 | } 22 | 23 | fn main() { 24 | // A stack of mementos. 25 | let mut history = Vec::::new(); 26 | 27 | let mut originator = Originator { state: 0 }; 28 | 29 | originator.state = 1; 30 | history.push(originator.save()); 31 | 32 | originator.state = 2; 33 | history.push(originator.save()); 34 | 35 | for moment in history.iter() { 36 | println!("{}", moment); 37 | } 38 | 39 | let originator = Originator::restore(&history.pop().unwrap()); 40 | println!("Restored to state: {}", originator.state); 41 | 42 | let originator = Originator::restore(&history.pop().unwrap()); 43 | println!("Restored to state: {}", originator.state); 44 | } 45 | -------------------------------------------------------------------------------- /behavioral/observer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "observer" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "observer" 8 | path = "main.rs" 9 | -------------------------------------------------------------------------------- /behavioral/observer/README.md: -------------------------------------------------------------------------------- 1 | # Observer 2 | 3 | _**Observer** is a behavioral design pattern that allows some objects to notify other objects about changes in their state._ 4 | 5 | **In Rust**, a convenient way to define a subscriber is to have **a function** 6 | as a callable object with complex logic passing it to a event publisher. 7 | 8 | In this Observer example, Subscribers are either **a lambda function** or 9 | **an explicit function** subscribed to the event. Explicit function objects could be also unsubscribed (although, there could be limitations for some function types). 10 | 11 | ## How to Run 12 | 13 | ```bash 14 | cargo run --bin observer 15 | ``` 16 | 17 | ## Execution Result 18 | 19 | ``` 20 | Save log to /path/to/log/file.txt: Load file test1.txt 21 | Save log to /path/to/log/file.txt: Load file test2.txt 22 | Email to admin@example.com: Save file test2.txt 23 | ``` 24 | 25 | ## Reference 26 | 27 | [Observer in Java (Example)](https://refactoring.guru/design-patterns/observer/java/example) 28 | -------------------------------------------------------------------------------- /behavioral/observer/editor.rs: -------------------------------------------------------------------------------- 1 | use crate::observer::{Event, Publisher}; 2 | 3 | /// Editor has its own logic and it utilizes a publisher 4 | /// to operate with subscribers and events. 5 | #[derive(Default)] 6 | pub struct Editor { 7 | publisher: Publisher, 8 | file_path: String, 9 | } 10 | 11 | impl Editor { 12 | pub fn events(&mut self) -> &mut Publisher { 13 | &mut self.publisher 14 | } 15 | 16 | pub fn load(&mut self, path: String) { 17 | self.file_path = path.clone(); 18 | self.publisher.notify(Event::Load, path); 19 | } 20 | 21 | pub fn save(&self) { 22 | self.publisher.notify(Event::Save, self.file_path.clone()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /behavioral/observer/main.rs: -------------------------------------------------------------------------------- 1 | use editor::Editor; 2 | use observer::Event; 3 | 4 | mod editor; 5 | mod observer; 6 | 7 | fn main() { 8 | let mut editor = Editor::default(); 9 | 10 | editor.events().subscribe(Event::Load, |file_path| { 11 | let log = "/path/to/log/file.txt".to_string(); 12 | println!("Save log to {}: Load file {}", log, file_path); 13 | }); 14 | 15 | editor.events().subscribe(Event::Save, save_listener); 16 | 17 | editor.load("test1.txt".into()); 18 | editor.load("test2.txt".into()); 19 | editor.save(); 20 | 21 | editor.events().unsubscribe(Event::Save, save_listener); 22 | editor.save(); 23 | } 24 | 25 | fn save_listener(file_path: String) { 26 | let email = "admin@example.com".to_string(); 27 | println!("Email to {}: Save file {}", email, file_path); 28 | } 29 | -------------------------------------------------------------------------------- /behavioral/observer/observer.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | /// An event type. 4 | #[derive(PartialEq, Eq, Hash, Clone)] 5 | pub enum Event { 6 | Load, 7 | Save, 8 | } 9 | 10 | /// A subscriber (listener) has type of a callable function. 11 | pub type Subscriber = fn(file_path: String); 12 | 13 | /// Publisher sends events to subscribers (listeners). 14 | #[derive(Default)] 15 | pub struct Publisher { 16 | events: HashMap>, 17 | } 18 | 19 | impl Publisher { 20 | pub fn subscribe(&mut self, event_type: Event, listener: Subscriber) { 21 | self.events.entry(event_type.clone()).or_default(); 22 | self.events.get_mut(&event_type).unwrap().push(listener); 23 | } 24 | 25 | pub fn unsubscribe(&mut self, event_type: Event, listener: Subscriber) { 26 | self.events 27 | .get_mut(&event_type) 28 | .unwrap() 29 | .retain(|&x| x != listener); 30 | } 31 | 32 | pub fn notify(&self, event_type: Event, file_path: String) { 33 | let listeners = self.events.get(&event_type).unwrap(); 34 | for listener in listeners { 35 | listener(file_path.clone()); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /behavioral/state/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "state" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "state" 8 | path = "main.rs" 9 | 10 | [dependencies] 11 | cursive = {version = "0.19", default-features = false, features = ["termion-backend"]} 12 | -------------------------------------------------------------------------------- /behavioral/state/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # State 4 | 5 | _The **State** pattern is related to a finite-state machine (FSM) concept, 6 | however, instead of implementing a lot of conditional statements, each state is represented by a separate type that implements a common state trait._ 7 | 8 | Transitions between states depend on the particular trait implementation for 9 | each state type. 10 | 11 | The State Pattern in Rust is described in detail in _The Rust Book_: 12 | https://doc.rust-lang.org/book/ch17-03-oo-design-patterns.html 13 | 14 | ## Music Player 15 | 16 | ```bash 17 | cargo run --bin state 18 | ``` 19 | 20 | Press buttons, ESC for exit, enjoy! 21 | 22 | ## Screenshots 23 | 24 | | | | 25 | | ------------------------------ | ------------------------------ | 26 | | ![Stopped](images/stopped.png) | ![Playing](images/playing.png) | 27 | 28 | ## Walkthrough 29 | 30 | Let's build a music player with the following state transitions: 31 | 32 | ![Music Player State Machine](images/state_machine.jpg) 33 | 34 | There is a base trait `State` with `play` and `stop` methods which make state 35 | transitions: 36 | 37 | ```rust 38 | pub trait State { 39 | fn play(self: Box, player: &mut Player) -> Box; 40 | fn stop(self: Box, player: &mut Player) -> Box; 41 | } 42 | ``` 43 | 44 | `next` and `prev` don't change state, there are default implementations 45 | in a separate `impl dyn State` block that cannot be overridden. 46 | 47 | ```rust 48 | impl dyn State { 49 | pub fn next(self: Box, player: &mut Player) -> Box { 50 | self 51 | } 52 | 53 | pub fn prev(self: Box, player: &mut Player) -> Box { 54 | self 55 | } 56 | } 57 | ``` 58 | 59 | Every state is a type implementing the `trait State`: 60 | 61 | ```rust 62 | pub struct StoppedState; 63 | pub struct PausedState; 64 | pub struct PlayingState; 65 | 66 | impl State for StoppedState { 67 | ... 68 | } 69 | 70 | impl State for PausedState { 71 | ... 72 | } 73 | ``` 74 | 75 | Anyways, it works as follows: 76 | 77 | ```rust 78 | let state = Box::new(StoppedState); // StoppedState. 79 | let state = state.play(&mut player); // StoppedState -> PlayingState. 80 | let state = state.play(&mut player); // PlayingState -> PausedState. 81 | ``` 82 | 83 | Here, the same action `play` makes a transition to different states depending 84 | on where it's called from: 85 | 86 | 1. `StoppedState`'s implementation of `play` starts playback and returns 87 | `PlayingState`. 88 | 89 | ```rust 90 | fn play(self: Box, player: &mut Player) -> Box { 91 | player.play(); 92 | 93 | // Stopped -> Playing. 94 | Box::new(PlayingState) 95 | } 96 | ``` 97 | 98 | 2. `PlayingState` pauses playback after hitting the "play" button again: 99 | 100 | ```rust 101 | fn play(self: Box, player: &mut Player) -> Box { 102 | player.pause(); 103 | 104 | // Playing -> Paused. 105 | Box::new(PausedState) 106 | } 107 | ``` 108 | 109 | 💡 The methods are defined with a special `self: Box` notation. 110 | 111 | Why is that? 112 | 113 | 1. First, `self` is not a reference, it means that the method is a "one shot", 114 | it consumes `self` and exchanges onto another state returning `Box`. 115 | 2. Second, the method consumes the boxed object like `Box` and 116 | not an object of a concrete type like `PlayingState`, because the concrete 117 | state is unknown at compile time. 118 | 119 | ## Reference 120 | 121 | [State Pattern in Java (Example)](https://refactoring.guru/design-patterns/state/java/example) 122 | -------------------------------------------------------------------------------- /behavioral/state/images/playing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RefactoringGuru/design-patterns-rust/0ac81c6e1446ddb333cd4ddef10403d31f7ad23b/behavioral/state/images/playing.png -------------------------------------------------------------------------------- /behavioral/state/images/state_machine.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RefactoringGuru/design-patterns-rust/0ac81c6e1446ddb333cd4ddef10403d31f7ad23b/behavioral/state/images/state_machine.jpg -------------------------------------------------------------------------------- /behavioral/state/images/stopped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RefactoringGuru/design-patterns-rust/0ac81c6e1446ddb333cd4ddef10403d31f7ad23b/behavioral/state/images/stopped.png -------------------------------------------------------------------------------- /behavioral/state/main.rs: -------------------------------------------------------------------------------- 1 | mod player; 2 | mod state; 3 | 4 | use cursive::{ 5 | event::Key, 6 | view::Nameable, 7 | views::{Dialog, TextView}, 8 | Cursive, 9 | }; 10 | use player::Player; 11 | use state::{State, StoppedState}; 12 | 13 | // Application context: a music player and a state. 14 | struct PlayerApplication { 15 | player: Player, 16 | state: Box, 17 | } 18 | 19 | fn main() { 20 | let mut app = cursive::default(); 21 | 22 | app.set_user_data(PlayerApplication { 23 | player: Player::default(), 24 | state: Box::new(StoppedState), 25 | }); 26 | 27 | app.add_layer( 28 | Dialog::around(TextView::new("Press Play").with_name("Player Status")) 29 | .title("Music Player") 30 | .button("Play", |s| execute(s, "Play")) 31 | .button("Stop", |s| execute(s, "Stop")) 32 | .button("Prev", |s| execute(s, "Prev")) 33 | .button("Next", |s| execute(s, "Next")), 34 | ); 35 | 36 | app.add_global_callback(Key::Esc, |s| s.quit()); 37 | 38 | app.run(); 39 | } 40 | 41 | fn execute(s: &mut Cursive, button: &'static str) { 42 | let PlayerApplication { 43 | mut player, 44 | mut state, 45 | } = s.take_user_data().unwrap(); 46 | 47 | let mut view = s.find_name::("Player Status").unwrap(); 48 | 49 | // Here is how state mechanics work: the previous state 50 | // executes an action and returns a new state. 51 | // Each state has all 4 operations but reacts differently. 52 | state = match button { 53 | "Play" => state.play(&mut player), 54 | "Stop" => state.stop(&mut player), 55 | "Prev" => state.prev(&mut player), 56 | "Next" => state.next(&mut player), 57 | _ => unreachable!(), 58 | }; 59 | 60 | state.render(&player, &mut view); 61 | 62 | s.set_user_data(PlayerApplication { player, state }); 63 | } 64 | -------------------------------------------------------------------------------- /behavioral/state/player.rs: -------------------------------------------------------------------------------- 1 | /// A music track. 2 | pub struct Track { 3 | pub title: String, 4 | pub duration: u32, 5 | cursor: u32, 6 | } 7 | 8 | impl Track { 9 | pub fn new(title: &'static str, duration: u32) -> Self { 10 | Self { 11 | title: title.into(), 12 | duration, 13 | cursor: 0, 14 | } 15 | } 16 | } 17 | 18 | /// A music player holds a playlist and it can do basic operations over it. 19 | pub struct Player { 20 | playlist: Vec, 21 | current_track: usize, 22 | _volume: u8, 23 | } 24 | 25 | impl Default for Player { 26 | fn default() -> Self { 27 | Self { 28 | playlist: vec![ 29 | Track::new("Track 1", 180), 30 | Track::new("Track 2", 165), 31 | Track::new("Track 3", 197), 32 | Track::new("Track 4", 205), 33 | ], 34 | current_track: 0, 35 | _volume: 25, 36 | } 37 | } 38 | } 39 | 40 | impl Player { 41 | pub fn next_track(&mut self) { 42 | self.current_track = (self.current_track + 1) % self.playlist.len(); 43 | } 44 | 45 | pub fn prev_track(&mut self) { 46 | self.current_track = (self.playlist.len() + self.current_track - 1) % self.playlist.len(); 47 | } 48 | 49 | pub fn play(&mut self) { 50 | self.track_mut().cursor = 10; // Playback imitation. 51 | } 52 | 53 | pub fn pause(&mut self) { 54 | self.track_mut().cursor = 43; // Paused at some moment. 55 | } 56 | 57 | pub fn rewind(&mut self) { 58 | self.track_mut().cursor = 0; 59 | } 60 | 61 | pub fn track(&self) -> &Track { 62 | &self.playlist[self.current_track] 63 | } 64 | 65 | fn track_mut(&mut self) -> &mut Track { 66 | &mut self.playlist[self.current_track] 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /behavioral/state/state.rs: -------------------------------------------------------------------------------- 1 | use cursive::views::TextView; 2 | 3 | use crate::player::Player; 4 | 5 | pub struct StoppedState; 6 | pub struct PausedState; 7 | pub struct PlayingState; 8 | 9 | /// There is a base `State` trait with methods `play` and `stop` which make 10 | /// state transitions. There are also `next` and `prev` methods in a separate 11 | /// `impl dyn State` block below, those are default implementations 12 | /// that cannot be overridden. 13 | /// 14 | /// What is the `self: Box` notation? We use the state as follows: 15 | /// ```rust 16 | /// let prev_state = Box::new(PlayingState); 17 | /// let next_state = prev_state.play(&mut player); 18 | /// ``` 19 | /// A method `play` receives a whole `Box` object, 20 | /// and not just `PlayingState`. The previous state "disappears" in the method, 21 | /// in turn, it returns a new `Box` state object. 22 | pub trait State { 23 | fn play(self: Box, player: &mut Player) -> Box; 24 | fn stop(self: Box, player: &mut Player) -> Box; 25 | fn render(&self, player: &Player, view: &mut TextView); 26 | } 27 | 28 | impl State for StoppedState { 29 | fn play(self: Box, player: &mut Player) -> Box { 30 | player.play(); 31 | 32 | // Stopped -> Playing. 33 | Box::new(PlayingState) 34 | } 35 | 36 | fn stop(self: Box, _: &mut Player) -> Box { 37 | // Change no state. 38 | self 39 | } 40 | 41 | fn render(&self, _: &Player, view: &mut TextView) { 42 | view.set_content("[Stopped] Press 'Play'") 43 | } 44 | } 45 | 46 | impl State for PausedState { 47 | fn play(self: Box, player: &mut Player) -> Box { 48 | player.pause(); 49 | 50 | // Paused -> Playing. 51 | Box::new(PlayingState) 52 | } 53 | 54 | fn stop(self: Box, player: &mut Player) -> Box { 55 | player.pause(); 56 | player.rewind(); 57 | 58 | // Paused -> Stopped. 59 | Box::new(StoppedState) 60 | } 61 | 62 | fn render(&self, player: &Player, view: &mut TextView) { 63 | view.set_content(format!( 64 | "[Paused] {} - {} sec", 65 | player.track().title, 66 | player.track().duration 67 | )) 68 | } 69 | } 70 | 71 | impl State for PlayingState { 72 | fn play(self: Box, player: &mut Player) -> Box { 73 | player.pause(); 74 | 75 | // Playing -> Paused. 76 | Box::new(PausedState) 77 | } 78 | 79 | fn stop(self: Box, player: &mut Player) -> Box { 80 | player.pause(); 81 | player.rewind(); 82 | 83 | // Playing -> Stopped. 84 | Box::new(StoppedState) 85 | } 86 | 87 | fn render(&self, player: &Player, view: &mut TextView) { 88 | view.set_content(format!( 89 | "[Playing] {} - {} sec", 90 | player.track().title, 91 | player.track().duration 92 | )) 93 | } 94 | } 95 | 96 | // Default "next" and "prev" implementations for the trait. 97 | impl dyn State { 98 | pub fn next(self: Box, player: &mut Player) -> Box { 99 | player.next_track(); 100 | 101 | // Change no state. 102 | self 103 | } 104 | 105 | pub fn prev(self: Box, player: &mut Player) -> Box { 106 | player.prev_track(); 107 | 108 | // Change no state. 109 | self 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /behavioral/strategy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "strategy" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "strategy" 8 | path = "conceptual.rs" 9 | 10 | [[bin]] 11 | name = "strategy-func" 12 | path = "functional.rs" 13 | -------------------------------------------------------------------------------- /behavioral/strategy/README.md: -------------------------------------------------------------------------------- 1 | # Strategy 2 | 3 | _**Strategy** turns a set of behaviors into objects and makes them 4 | interchangeable inside original context object._ 5 | 6 | ## `conceptual.rs` 7 | 8 | A conceptual Strategy example via traits. 9 | 10 | ```bash 11 | cargo run --bin strategy 12 | ``` 13 | 14 | Output: 15 | 16 | ``` 17 | Walking route from Home to Club: 4 km, 30 min 18 | Walking route from Club to Work: 4 km, 30 min 19 | Public transport route from Home to Club: 3 km, 5 min 20 | Public transport route from Club to Work: 3 km, 5 min 21 | ``` 22 | 23 | ## 🦀 `functional.rs` 24 | 25 | Functions and closures simplify Strategy implementation as you can 26 | inject behavior right into the object without complex interface definition. 27 | 28 | ```bash 29 | cargo run --bin strategy-func 30 | ``` 31 | 32 | It seems that Strategy is often implicitly and widely used in the modern 33 | development with Rust, e.g. it's just like iterators work: 34 | 35 | ```rust 36 | let a = [0i32, 1, 2]; 37 | 38 | let mut iter = a.iter().filter(|x| x.is_positive()); 39 | ``` 40 | 41 | Output: 42 | 43 | ``` 44 | Walking route from Home to Club: 4 km, 30 min 45 | Walking route from Club to Work: 4 km, 30 min 46 | Public transport route from Home to Club: 3 km, 5 min 47 | Public transport route from Club to Work: 3 km, 5 min 48 | Specific route from Home to Club 49 | Specific route from Club to Work 50 | ``` 51 | -------------------------------------------------------------------------------- /behavioral/strategy/conceptual.rs: -------------------------------------------------------------------------------- 1 | /// Defines an injectable strategy for building routes. 2 | trait RouteStrategy { 3 | fn build_route(&self, from: &str, to: &str); 4 | } 5 | 6 | struct WalkingStrategy; 7 | 8 | impl RouteStrategy for WalkingStrategy { 9 | fn build_route(&self, from: &str, to: &str) { 10 | println!("Walking route from {} to {}: 4 km, 30 min", from, to); 11 | } 12 | } 13 | 14 | struct PublicTransportStrategy; 15 | 16 | impl RouteStrategy for PublicTransportStrategy { 17 | fn build_route(&self, from: &str, to: &str) { 18 | println!( 19 | "Public transport route from {} to {}: 3 km, 5 min", 20 | from, to 21 | ); 22 | } 23 | } 24 | 25 | struct Navigator { 26 | route_strategy: T, 27 | } 28 | 29 | impl Navigator { 30 | pub fn new(route_strategy: T) -> Self { 31 | Self { route_strategy } 32 | } 33 | 34 | pub fn route(&self, from: &str, to: &str) { 35 | self.route_strategy.build_route(from, to); 36 | } 37 | } 38 | 39 | fn main() { 40 | let navigator = Navigator::new(WalkingStrategy); 41 | navigator.route("Home", "Club"); 42 | navigator.route("Club", "Work"); 43 | 44 | let navigator = Navigator::new(PublicTransportStrategy); 45 | navigator.route("Home", "Club"); 46 | navigator.route("Club", "Work"); 47 | } 48 | -------------------------------------------------------------------------------- /behavioral/strategy/functional.rs: -------------------------------------------------------------------------------- 1 | type RouteStrategy = fn(from: &str, to: &str); 2 | 3 | fn walking_strategy(from: &str, to: &str) { 4 | println!("Walking route from {} to {}: 4 km, 30 min", from, to); 5 | } 6 | 7 | fn public_transport_strategy(from: &str, to: &str) { 8 | println!( 9 | "Public transport route from {} to {}: 3 km, 5 min", 10 | from, to 11 | ); 12 | } 13 | 14 | struct Navigator { 15 | route_strategy: RouteStrategy, 16 | } 17 | 18 | impl Navigator { 19 | pub fn new(route_strategy: RouteStrategy) -> Self { 20 | Self { route_strategy } 21 | } 22 | 23 | pub fn route(&self, from: &str, to: &str) { 24 | (self.route_strategy)(from, to); 25 | } 26 | } 27 | 28 | fn main() { 29 | let navigator = Navigator::new(walking_strategy); 30 | navigator.route("Home", "Club"); 31 | navigator.route("Club", "Work"); 32 | 33 | let navigator = Navigator::new(public_transport_strategy); 34 | navigator.route("Home", "Club"); 35 | navigator.route("Club", "Work"); 36 | 37 | let navigator = Navigator::new(|from, to| println!("Specific route from {} to {}", from, to)); 38 | navigator.route("Home", "Club"); 39 | navigator.route("Club", "Work"); 40 | } 41 | -------------------------------------------------------------------------------- /behavioral/template-method/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "template-method" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "template-method" 8 | path = "main.rs" 9 | -------------------------------------------------------------------------------- /behavioral/template-method/README.md: -------------------------------------------------------------------------------- 1 | # Template Method 2 | 3 | _**Template Method** is a default implementation of a trait method that defines a skeleton 4 | of an algorithm, and other methods can be redefined in concrete types._ 5 | 6 | ## Conceptual Example 7 | 8 | A referenced example: [Template Method in Python](https://refactoring.guru/design-patterns/template-method/python/example) 9 | 10 | ```bash 11 | cargo run --bin template-method 12 | ``` 13 | 14 | Output: 15 | 16 | ``` 17 | Same client code can work with different concrete implementations: 18 | TemplateMethod says: I am doing the bulk of the work 19 | ConcreteStruct1 says: Implemented Operation1 20 | TemplateMethod says: But I let subclasses override some operations 21 | ConcreteStruct1 says: Implemented Operation2 22 | TemplateMethod says: But I am doing the bulk of the work anyway 23 | 24 | Same client code can work with different concrete implementations: 25 | TemplateMethod says: I am doing the bulk of the work 26 | ConcreteStruct2 says: Implemented Operation1 27 | TemplateMethod says: But I let subclasses override some operations 28 | ConcreteStruct2 says: Implemented Operation2 29 | TemplateMethod says: But I am doing the bulk of the work anyway 30 | ``` 31 | -------------------------------------------------------------------------------- /behavioral/template-method/main.rs: -------------------------------------------------------------------------------- 1 | trait TemplateMethod { 2 | fn template_method(&self) { 3 | self.base_operation1(); 4 | self.required_operations1(); 5 | self.base_operation2(); 6 | self.hook1(); 7 | self.required_operations2(); 8 | self.base_operation3(); 9 | self.hook2(); 10 | } 11 | 12 | fn base_operation1(&self) { 13 | println!("TemplateMethod says: I am doing the bulk of the work"); 14 | } 15 | 16 | fn base_operation2(&self) { 17 | println!("TemplateMethod says: But I let subclasses override some operations"); 18 | } 19 | 20 | fn base_operation3(&self) { 21 | println!("TemplateMethod says: But I am doing the bulk of the work anyway"); 22 | } 23 | 24 | fn hook1(&self) {} 25 | fn hook2(&self) {} 26 | 27 | fn required_operations1(&self); 28 | fn required_operations2(&self); 29 | } 30 | 31 | struct ConcreteStruct1; 32 | 33 | impl TemplateMethod for ConcreteStruct1 { 34 | fn required_operations1(&self) { 35 | println!("ConcreteStruct1 says: Implemented Operation1") 36 | } 37 | 38 | fn required_operations2(&self) { 39 | println!("ConcreteStruct1 says: Implemented Operation2") 40 | } 41 | } 42 | 43 | struct ConcreteStruct2; 44 | 45 | impl TemplateMethod for ConcreteStruct2 { 46 | fn required_operations1(&self) { 47 | println!("ConcreteStruct2 says: Implemented Operation1") 48 | } 49 | 50 | fn required_operations2(&self) { 51 | println!("ConcreteStruct2 says: Implemented Operation2") 52 | } 53 | } 54 | 55 | fn client_code(concrete: impl TemplateMethod) { 56 | concrete.template_method() 57 | } 58 | 59 | fn main() { 60 | println!("Same client code can work with different concrete implementations:"); 61 | client_code(ConcreteStruct1); 62 | println!(); 63 | 64 | println!("Same client code can work with different concrete implementations:"); 65 | client_code(ConcreteStruct2); 66 | } 67 | -------------------------------------------------------------------------------- /behavioral/visitor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "visitor" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | [[bin]] 11 | name = "visitor" 12 | path = "main.rs" 13 | -------------------------------------------------------------------------------- /behavioral/visitor/README.md: -------------------------------------------------------------------------------- 1 | # Visitor 2 | 3 | _**Visitor** lets you add “external” operations to a whole class hierarchy 4 | without changing the existing code of these classes._ 5 | 6 | A real-world example of the Visitor pattern is [serde.rs](https://serde.rs) deserialization 7 | model, see [Serde Data Model](https://serde.rs/data-model.html): 8 | 9 | 1. `Visitor` should be implemented for a deserializable type. 10 | 2. `Visitor` is passed to a `Deserializer` (an "Element" in terms of the Visitor Pattern), which accepts and drives the `Visitor` in order to construct a desired type. 11 | 12 | Let's reproduce this deserializing model in our example. 13 | 14 | ## How to Run 15 | 16 | ```bash 17 | cargo run --bin visitor 18 | ``` 19 | 20 | ## Execution Result 21 | 22 | ``` 23 | Ok(TwoValuesStruct { a: 123, b: 456 }) 24 | Ok(TwoValuesStruct { a: 123, b: 456 }) 25 | Ok(TwoValuesArray { ab: [123, 456] }) 26 | Error: parse_str unimplemented 27 | ``` 28 | -------------------------------------------------------------------------------- /behavioral/visitor/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | 3 | mod visitor; 4 | 5 | use visitor::Visitor; 6 | 7 | /// A struct of two integer values. 8 | /// 9 | /// It's going to be an output of `Visitor` trait which is defined for the type 10 | /// in `visitor.rs`. 11 | #[derive(Default, Debug)] 12 | pub struct TwoValuesStruct { 13 | a: i32, 14 | b: i32, 15 | } 16 | 17 | /// A struct of values array. 18 | /// 19 | /// It's going to be an output of `Visitor` trait which is defined for the type 20 | /// in `visitor.rs`. 21 | #[derive(Default, Debug)] 22 | pub struct TwoValuesArray { 23 | ab: [i32; 2], 24 | } 25 | 26 | /// `Deserializer` trait defines methods that can parse either a string or 27 | /// a vector, it accepts a visitor which knows how to construct a new object 28 | /// of a desired type (in our case, `TwoValuesArray` and `TwoValuesStruct`). 29 | trait Deserializer { 30 | fn create(visitor: V) -> Self; 31 | fn parse_str(&self, input: &str) -> Result { 32 | Err("parse_str is unimplemented") 33 | } 34 | fn parse_vec(&self, input: Vec) -> Result { 35 | Err("parse_vec is unimplemented") 36 | } 37 | } 38 | 39 | struct StringDeserializer { 40 | visitor: V, 41 | } 42 | 43 | impl Deserializer for StringDeserializer { 44 | fn create(visitor: V) -> Self { 45 | Self { visitor } 46 | } 47 | 48 | fn parse_str(&self, input: &str) -> Result { 49 | // In this case, in order to apply a visitor, a deserializer should do 50 | // some preparation. The visitor does its stuff, but it doesn't do everything. 51 | let input_vec = input 52 | .split_ascii_whitespace() 53 | .map(|x| x.parse().unwrap()) 54 | .collect(); 55 | 56 | Ok(self.visitor.visit_vec(input_vec)) 57 | } 58 | } 59 | 60 | struct VecDeserializer { 61 | visitor: V, 62 | } 63 | 64 | impl Deserializer for VecDeserializer { 65 | fn create(visitor: V) -> Self { 66 | Self { visitor } 67 | } 68 | 69 | fn parse_vec(&self, input: Vec) -> Result { 70 | Ok(self.visitor.visit_vec(input)) 71 | } 72 | } 73 | 74 | fn main() { 75 | let deserializer = StringDeserializer::create(TwoValuesStruct::default()); 76 | let result = deserializer.parse_str("123 456"); 77 | println!("{:?}", result); 78 | 79 | let deserializer = VecDeserializer::create(TwoValuesStruct::default()); 80 | let result = deserializer.parse_vec(vec![123, 456]); 81 | println!("{:?}", result); 82 | 83 | let deserializer = VecDeserializer::create(TwoValuesArray::default()); 84 | let result = deserializer.parse_vec(vec![123, 456]); 85 | println!("{:?}", result); 86 | 87 | println!( 88 | "Error: {}", 89 | deserializer.parse_str("123 456").err().unwrap() 90 | ) 91 | } 92 | -------------------------------------------------------------------------------- /behavioral/visitor/visitor.rs: -------------------------------------------------------------------------------- 1 | use crate::{TwoValuesArray, TwoValuesStruct}; 2 | 3 | /// Visitor can visit one type, do conversions, and output another type. 4 | /// 5 | /// It's not like all visitors must return a new type, it's just an example 6 | /// that demonstrates the technique. 7 | pub trait Visitor { 8 | type Value; 9 | 10 | /// Visits a vector of integers and outputs a desired type. 11 | fn visit_vec(&self, v: Vec) -> Self::Value; 12 | } 13 | 14 | /// Visitor implementation for a struct of two values. 15 | impl Visitor for TwoValuesStruct { 16 | type Value = TwoValuesStruct; 17 | 18 | fn visit_vec(&self, v: Vec) -> Self::Value { 19 | TwoValuesStruct { a: v[0], b: v[1] } 20 | } 21 | } 22 | 23 | /// Visitor implementation for a struct of values array. 24 | impl Visitor for TwoValuesArray { 25 | type Value = TwoValuesArray; 26 | 27 | fn visit_vec(&self, v: Vec) -> Self::Value { 28 | let mut ab = [0i32; 2]; 29 | 30 | ab[0] = v[0]; 31 | ab[1] = v[1]; 32 | 33 | TwoValuesArray { ab } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /creational/README.md: -------------------------------------------------------------------------------- 1 | # Behavioral Patterns 2 | 3 | _**Creational** design patterns provide various object creation mechanisms, which increase flexibility and reuse of existing code._ 4 | 5 | 👆 Each example contains its own README and instructions. -------------------------------------------------------------------------------- /creational/abstract-factory/README.md: -------------------------------------------------------------------------------- 1 | # Abstract Factory 2 | 3 | _**Abstract Factory** solves the problem of creating entire product families 4 | without specifying their concrete classes._ 5 | 6 | ## GUI Factory Example 7 | 8 | This example shows a GUI framework can organize its classes into independent 9 | libraries: 10 | 11 | 1. The `gui` library defines interfaces for all the components. 12 | It has no external dependencies. 13 | 2. The `windows-gui` library provides Windows implementation of the base GUI. 14 | Depends on `gui`. 15 | 3. The `macos-gui` library provides Mac OS implementation of the base GUI. 16 | Depends on `gui`. 17 | 18 | The `app` is a client application that can use several implementations of the 19 | GUI framework, depending on the current environment or configuration. 20 | However, most of the `app` code _**doesn't depend on specific types of GUI 21 | elements**_. All the client code works with GUI elements through abstract 22 | interfaces (traits) defined by the `gui` lib. 23 | 24 | There are also 2 approaches to implementing abstract factory in Rust: 25 | using generics (_static dispatch_) and using dynamic allocation 26 | (_dynamic dispatch_). 27 | 28 | ## `app/main.rs` 29 | 30 | Here, abstract factory is implemented via **generics** which allow the compiler 31 | to create a code that does NOT require dynamic dispatch in runtime. 32 | 33 | ```rust 34 | pub trait GuiFactory { 35 | type B: Button; 36 | type C: Checkbox; 37 | 38 | fn create_button(&self) -> Self::B; 39 | fn create_checkbox(&self) -> Self::C; 40 | } 41 | ``` 42 | 43 | ### How to Run 44 | 45 | ```bash 46 | cargo run --bin abstract-factory 47 | ``` 48 | 49 | ### Output 50 | 51 | ``` 52 | Windows button has pressed 53 | Windows button has pressed 54 | Windows checkbox has switched 55 | Windows checkbox has switched 56 | ``` 57 | 58 | ## `app-dyn/main.rs` 59 | 60 | If a concrete type of abstract factory is not known at the compilation time, 61 | then is should be implemented using `Box` pointers. 62 | 63 | ```rust 64 | pub trait GuiFactoryDynamic { 65 | fn create_button(&self) -> Box; 66 | fn create_checkbox(&self) -> Box; 67 | } 68 | ``` 69 | 70 | ### How to Run 71 | 72 | ```bash 73 | cargo run --bin abstract-factory-dyn 74 | ``` 75 | 76 | ### Output 77 | 78 | ``` 79 | MacOS button has pressed 80 | MacOS button has pressed 81 | MacOS button has pressed 82 | MacOS checkbox has switched 83 | MacOS checkbox has switched 84 | ``` 85 | 86 | ## Reference 87 | 88 | [Abstract Factory in Java (Example)](https://refactoring.guru/design-patterns/abstract-factory/java/example) 89 | -------------------------------------------------------------------------------- /creational/abstract-factory/app-dyn/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "abstract_factory-dyn" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "abstract-factory-dyn" 8 | path = "main.rs" 9 | 10 | [dependencies] 11 | gui = {path = "../gui", version = "0.1.0"} 12 | macos-gui = {path = "../macos-gui", version = "0.1.0"} 13 | windows-gui = {path = "../windows-gui", version = "0.1.0"} 14 | -------------------------------------------------------------------------------- /creational/abstract-factory/app-dyn/main.rs: -------------------------------------------------------------------------------- 1 | mod render; 2 | 3 | use render::render; 4 | 5 | use gui::GuiFactoryDynamic; 6 | use macos_gui::factory::MacFactory; 7 | use windows_gui::factory::WindowsFactory; 8 | 9 | fn main() { 10 | let windows = false; 11 | 12 | // Allocate a factory object in runtime depending on unpredictable input. 13 | let factory: &dyn GuiFactoryDynamic = if windows { 14 | &WindowsFactory 15 | } else { 16 | &MacFactory 17 | }; 18 | 19 | // Factory invocation can be inlined right here. 20 | let button = factory.create_button(); 21 | button.press(); 22 | 23 | // Factory object can be passed to a function as a parameter. 24 | render(factory); 25 | } 26 | -------------------------------------------------------------------------------- /creational/abstract-factory/app-dyn/render.rs: -------------------------------------------------------------------------------- 1 | //! The code demonstrates that it doesn't depend on a concrete 2 | //! factory implementation. 3 | 4 | use gui::GuiFactoryDynamic; 5 | 6 | /// Renders GUI. 7 | pub fn render(factory: &dyn GuiFactoryDynamic) { 8 | let button1 = factory.create_button(); 9 | let button2 = factory.create_button(); 10 | let checkbox1 = factory.create_checkbox(); 11 | let checkbox2 = factory.create_checkbox(); 12 | 13 | button1.press(); 14 | button2.press(); 15 | checkbox1.switch(); 16 | checkbox2.switch(); 17 | } 18 | -------------------------------------------------------------------------------- /creational/abstract-factory/app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "abstract_factory" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "abstract-factory" 8 | path = "main.rs" 9 | 10 | [dependencies] 11 | gui = {path = "../gui", version = "0.1.0"} 12 | macos-gui = {path = "../macos-gui", version = "0.1.0"} 13 | windows-gui = {path = "../windows-gui", version = "0.1.0"} 14 | -------------------------------------------------------------------------------- /creational/abstract-factory/app/main.rs: -------------------------------------------------------------------------------- 1 | mod render; 2 | 3 | use render::render; 4 | 5 | use macos_gui::factory::MacFactory; 6 | use windows_gui::factory::WindowsFactory; 7 | 8 | fn main() { 9 | let windows = true; 10 | 11 | if windows { 12 | render(WindowsFactory); 13 | } else { 14 | render(MacFactory); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /creational/abstract-factory/app/render.rs: -------------------------------------------------------------------------------- 1 | //! The code demonstrates that it doesn't depend on a concrete 2 | //! factory implementation. 3 | 4 | use gui::GuiFactory; 5 | 6 | // Renders GUI. Factory object must be passed as a parameter to such the 7 | // generic function with factory invocation to utilize static dispatch. 8 | pub fn render(factory: impl GuiFactory) { 9 | let button1 = factory.create_button(); 10 | let button2 = factory.create_button(); 11 | let checkbox1 = factory.create_checkbox(); 12 | let checkbox2 = factory.create_checkbox(); 13 | 14 | use gui::{Button, Checkbox}; 15 | 16 | button1.press(); 17 | button2.press(); 18 | checkbox1.switch(); 19 | checkbox2.switch(); 20 | } 21 | -------------------------------------------------------------------------------- /creational/abstract-factory/gui/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "gui" 4 | version = "0.1.0" 5 | 6 | [lib] 7 | path = "lib.rs" 8 | -------------------------------------------------------------------------------- /creational/abstract-factory/gui/lib.rs: -------------------------------------------------------------------------------- 1 | pub trait Button { 2 | fn press(&self); 3 | } 4 | 5 | pub trait Checkbox { 6 | fn switch(&self); 7 | } 8 | 9 | /// Abstract Factory defined using generics. 10 | pub trait GuiFactory { 11 | type B: Button; 12 | type C: Checkbox; 13 | 14 | fn create_button(&self) -> Self::B; 15 | fn create_checkbox(&self) -> Self::C; 16 | } 17 | 18 | /// Abstract Factory defined using Box pointer. 19 | pub trait GuiFactoryDynamic { 20 | fn create_button(&self) -> Box; 21 | fn create_checkbox(&self) -> Box; 22 | } 23 | -------------------------------------------------------------------------------- /creational/abstract-factory/macos-gui/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "macos-gui" 4 | version = "0.1.0" 5 | 6 | [lib] 7 | path = "lib.rs" 8 | 9 | [dependencies] 10 | gui = {path = "../gui", version = "0.1.0"} 11 | -------------------------------------------------------------------------------- /creational/abstract-factory/macos-gui/button.rs: -------------------------------------------------------------------------------- 1 | use gui::Button; 2 | 3 | pub struct MacButton; 4 | 5 | impl Button for MacButton { 6 | fn press(&self) { 7 | println!("MacOS button has pressed"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /creational/abstract-factory/macos-gui/checkbox.rs: -------------------------------------------------------------------------------- 1 | use gui::Checkbox; 2 | 3 | pub struct MacCheckbox; 4 | 5 | impl Checkbox for MacCheckbox { 6 | fn switch(&self) { 7 | println!("MacOS checkbox has switched"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /creational/abstract-factory/macos-gui/factory.rs: -------------------------------------------------------------------------------- 1 | use gui::{Button, Checkbox, GuiFactory, GuiFactoryDynamic}; 2 | 3 | use crate::{button::MacButton, checkbox::MacCheckbox}; 4 | 5 | pub struct MacFactory; 6 | 7 | impl GuiFactory for MacFactory { 8 | type B = MacButton; 9 | type C = MacCheckbox; 10 | 11 | fn create_button(&self) -> MacButton { 12 | MacButton 13 | } 14 | 15 | fn create_checkbox(&self) -> MacCheckbox { 16 | MacCheckbox 17 | } 18 | } 19 | 20 | impl GuiFactoryDynamic for MacFactory { 21 | fn create_button(&self) -> Box { 22 | Box::new(MacButton) 23 | } 24 | 25 | fn create_checkbox(&self) -> Box { 26 | Box::new(MacCheckbox) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /creational/abstract-factory/macos-gui/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod button; 2 | pub mod checkbox; 3 | pub mod factory; 4 | -------------------------------------------------------------------------------- /creational/abstract-factory/windows-gui/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "windows-gui" 4 | version = "0.1.0" 5 | 6 | [lib] 7 | path = "lib.rs" 8 | 9 | [dependencies] 10 | gui = {path = "../gui", version = "0.1.0"} 11 | -------------------------------------------------------------------------------- /creational/abstract-factory/windows-gui/button.rs: -------------------------------------------------------------------------------- 1 | use gui::Button; 2 | 3 | pub struct WindowsButton; 4 | 5 | impl Button for WindowsButton { 6 | fn press(&self) { 7 | println!("Windows button has pressed"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /creational/abstract-factory/windows-gui/checkbox.rs: -------------------------------------------------------------------------------- 1 | use gui::Checkbox; 2 | 3 | pub struct WindowsCheckbox; 4 | 5 | impl Checkbox for WindowsCheckbox { 6 | fn switch(&self) { 7 | println!("Windows checkbox has switched"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /creational/abstract-factory/windows-gui/factory.rs: -------------------------------------------------------------------------------- 1 | use gui::{Button, Checkbox, GuiFactory, GuiFactoryDynamic}; 2 | 3 | use crate::{button::WindowsButton, checkbox::WindowsCheckbox}; 4 | 5 | pub struct WindowsFactory; 6 | 7 | impl GuiFactory for WindowsFactory { 8 | type B = WindowsButton; 9 | type C = WindowsCheckbox; 10 | 11 | fn create_button(&self) -> WindowsButton { 12 | WindowsButton 13 | } 14 | 15 | fn create_checkbox(&self) -> WindowsCheckbox { 16 | WindowsCheckbox 17 | } 18 | } 19 | 20 | impl GuiFactoryDynamic for WindowsFactory { 21 | fn create_button(&self) -> Box { 22 | Box::new(WindowsButton) 23 | } 24 | 25 | fn create_checkbox(&self) -> Box { 26 | Box::new(WindowsCheckbox) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /creational/abstract-factory/windows-gui/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod button; 2 | pub mod checkbox; 3 | pub mod factory; 4 | -------------------------------------------------------------------------------- /creational/builder/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "builder" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "builder" 8 | path = "main.rs" 9 | -------------------------------------------------------------------------------- /creational/builder/README.md: -------------------------------------------------------------------------------- 1 | # Builder 2 | 3 | _**Builder** is a creational design pattern, which allows constructing of complex 4 | objects step by step._ 5 | 6 | In this example, [`trait Builder`](builders/mod.rs) defines how to assemble 7 | a car. However, depending on the builder implementation, a constructed object 8 | can be either a car, or a car manual. 9 | 10 | 💡 The **Builder** design pattern is not the same as a **Fluent Interface** idiom, 11 | although Rust developers sometimes use those terms interchangeably. 12 | 13 | 1. **Fluent Interface** idiom is a way to chain methods for constructing or 14 | modifying an object using the following approach: 15 | `let car = Car::default().places(5).gas(30)`. 16 | It's pretty useful for constructing an object. Still, it's not the Builder. 17 | 2. **Builder** is a pattern with a common building trait but with different 18 | building implementations. At the same time, Fluent Interface can be used 19 | together with the Builder pattern for a better code design. 20 | 21 | ## How to Run 22 | 23 | ```bash 24 | cargo run --bin builder 25 | ``` 26 | 27 | ## Output 28 | 29 | ``` 30 | Car built: SportsCar 31 | 32 | Car manual built: 33 | Type of car: CityCar 34 | Count of seats: 2 35 | Engine: volume - 1.2; mileage - 0 36 | Transmission: Automatic 37 | GPS Navigator: Functional 38 | ``` 39 | 40 | ## Reference 41 | 42 | This example is reproducing the [Builder Example in Java](https://refactoring.guru/design-patterns/builder/java/example). -------------------------------------------------------------------------------- /creational/builder/builders/car.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | cars::Car, 3 | components::{CarType, Engine, GpsNavigator, Transmission}, 4 | }; 5 | 6 | use super::Builder; 7 | 8 | pub const DEFAULT_FUEL: f64 = 5f64; 9 | 10 | #[derive(Default)] 11 | pub struct CarBuilder { 12 | car_type: Option, 13 | engine: Option, 14 | gps_navigator: Option, 15 | seats: Option, 16 | transmission: Option, 17 | } 18 | 19 | impl Builder for CarBuilder { 20 | type OutputType = Car; 21 | 22 | fn set_car_type(&mut self, car_type: CarType) { 23 | self.car_type = Some(car_type); 24 | } 25 | 26 | fn set_engine(&mut self, engine: Engine) { 27 | self.engine = Some(engine); 28 | } 29 | 30 | fn set_gsp_navigator(&mut self, gps_navigator: GpsNavigator) { 31 | self.gps_navigator = Some(gps_navigator); 32 | } 33 | 34 | fn set_seats(&mut self, seats: u16) { 35 | self.seats = Some(seats); 36 | } 37 | 38 | fn set_transmission(&mut self, transmission: Transmission) { 39 | self.transmission = Some(transmission); 40 | } 41 | 42 | fn build(self) -> Car { 43 | Car::new( 44 | self.car_type.expect("Please, set a car type"), 45 | self.seats.expect("Please, set a number of seats"), 46 | self.engine.expect("Please, set an engine configuration"), 47 | self.transmission.expect("Please, set up transmission"), 48 | self.gps_navigator, 49 | DEFAULT_FUEL, 50 | ) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /creational/builder/builders/car_manual.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | cars::Manual, 3 | components::{CarType, Engine, GpsNavigator, Transmission}, 4 | }; 5 | 6 | use super::Builder; 7 | 8 | #[derive(Default)] 9 | pub struct CarManualBuilder { 10 | car_type: Option, 11 | engine: Option, 12 | gps_navigator: Option, 13 | seats: Option, 14 | transmission: Option, 15 | } 16 | 17 | /// Builds a car manual instead of an actual car. 18 | impl Builder for CarManualBuilder { 19 | type OutputType = Manual; 20 | 21 | fn set_car_type(&mut self, car_type: CarType) { 22 | self.car_type = Some(car_type); 23 | } 24 | 25 | fn set_engine(&mut self, engine: Engine) { 26 | self.engine = Some(engine); 27 | } 28 | 29 | fn set_gsp_navigator(&mut self, gps_navigator: GpsNavigator) { 30 | self.gps_navigator = Some(gps_navigator); 31 | } 32 | 33 | fn set_seats(&mut self, seats: u16) { 34 | self.seats = Some(seats); 35 | } 36 | 37 | fn set_transmission(&mut self, transmission: Transmission) { 38 | self.transmission = Some(transmission); 39 | } 40 | 41 | fn build(self) -> Manual { 42 | Manual::new( 43 | self.car_type.expect("Please, set a car type"), 44 | self.seats.expect("Please, set a number of seats"), 45 | self.engine.expect("Please, set an engine configuration"), 46 | self.transmission.expect("Please, set up transmission"), 47 | self.gps_navigator, 48 | ) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /creational/builder/builders/mod.rs: -------------------------------------------------------------------------------- 1 | mod car; 2 | mod car_manual; 3 | 4 | use crate::components::{CarType, Engine, GpsNavigator, Transmission}; 5 | 6 | /// Builder defines how to assemble a car. 7 | pub trait Builder { 8 | type OutputType; 9 | fn set_car_type(&mut self, car_type: CarType); 10 | fn set_seats(&mut self, seats: u16); 11 | fn set_engine(&mut self, engine: Engine); 12 | fn set_transmission(&mut self, transmission: Transmission); 13 | fn set_gsp_navigator(&mut self, gps_navigator: GpsNavigator); 14 | fn build(self) -> Self::OutputType; 15 | } 16 | 17 | pub use car::CarBuilder; 18 | pub use car_manual::CarManualBuilder; 19 | -------------------------------------------------------------------------------- /creational/builder/cars/car.rs: -------------------------------------------------------------------------------- 1 | use crate::components::{CarType, Engine, GpsNavigator, Transmission}; 2 | 3 | pub struct Car { 4 | car_type: CarType, 5 | seats: u16, 6 | engine: Engine, 7 | transmission: Transmission, 8 | gps_navigator: Option, 9 | fuel: f64, 10 | } 11 | 12 | impl Car { 13 | pub fn new( 14 | car_type: CarType, 15 | seats: u16, 16 | engine: Engine, 17 | transmission: Transmission, 18 | gps_navigator: Option, 19 | fuel: f64, 20 | ) -> Self { 21 | Self { 22 | car_type, 23 | seats, 24 | engine, 25 | transmission, 26 | gps_navigator, 27 | fuel, 28 | } 29 | } 30 | 31 | pub fn car_type(&self) -> CarType { 32 | self.car_type 33 | } 34 | 35 | pub fn fuel(&self) -> f64 { 36 | self.fuel 37 | } 38 | 39 | pub fn set_fuel(&mut self, fuel: f64) { 40 | self.fuel = fuel; 41 | } 42 | 43 | pub fn seats(&self) -> u16 { 44 | self.seats 45 | } 46 | 47 | pub fn engine(&self) -> &Engine { 48 | &self.engine 49 | } 50 | 51 | pub fn transmission(&self) -> &Transmission { 52 | &self.transmission 53 | } 54 | 55 | pub fn gps_navigator(&self) -> &Option { 56 | &self.gps_navigator 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /creational/builder/cars/manual.rs: -------------------------------------------------------------------------------- 1 | use crate::components::{CarType, Engine, GpsNavigator, Transmission}; 2 | 3 | pub struct Manual { 4 | car_type: CarType, 5 | seats: u16, 6 | engine: Engine, 7 | transmission: Transmission, 8 | gps_navigator: Option, 9 | } 10 | 11 | impl Manual { 12 | pub fn new( 13 | car_type: CarType, 14 | seats: u16, 15 | engine: Engine, 16 | transmission: Transmission, 17 | gps_navigator: Option, 18 | ) -> Self { 19 | Self { 20 | car_type, 21 | seats, 22 | engine, 23 | transmission, 24 | gps_navigator, 25 | } 26 | } 27 | } 28 | 29 | impl std::fmt::Display for Manual { 30 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 31 | writeln!(f, "Type of car: {:?}", self.car_type)?; 32 | writeln!(f, "Count of seats: {}", self.seats)?; 33 | writeln!( 34 | f, 35 | "Engine: volume - {}; mileage - {}", 36 | self.engine.volume(), 37 | self.engine.mileage() 38 | )?; 39 | writeln!(f, "Transmission: {:?}", self.transmission)?; 40 | match self.gps_navigator { 41 | Some(_) => writeln!(f, "GPS Navigator: Functional")?, 42 | None => writeln!(f, "GPS Navigator: N/A")?, 43 | }; 44 | Ok(()) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /creational/builder/cars/mod.rs: -------------------------------------------------------------------------------- 1 | mod car; 2 | mod manual; 3 | 4 | pub use car::Car; 5 | pub use manual::Manual; 6 | -------------------------------------------------------------------------------- /creational/builder/components.rs: -------------------------------------------------------------------------------- 1 | #[derive(Copy, Clone, Debug)] 2 | pub enum CarType { 3 | CityCar, 4 | SportsCar, 5 | Suv, 6 | } 7 | 8 | #[derive(Debug)] 9 | pub enum Transmission { 10 | SingleSpeed, 11 | Manual, 12 | Automatic, 13 | SemiAutomatic, 14 | } 15 | 16 | pub struct Engine { 17 | volume: f64, 18 | mileage: f64, 19 | started: bool, 20 | } 21 | 22 | impl Engine { 23 | pub fn new(volume: f64, mileage: f64) -> Self { 24 | Self { 25 | volume, 26 | mileage, 27 | started: false, 28 | } 29 | } 30 | 31 | pub fn on(&mut self) { 32 | self.started = true; 33 | } 34 | 35 | pub fn off(&mut self) { 36 | self.started = false; 37 | } 38 | 39 | pub fn started(&self) -> bool { 40 | self.started 41 | } 42 | 43 | pub fn volume(&self) -> f64 { 44 | self.volume 45 | } 46 | 47 | pub fn mileage(&self) -> f64 { 48 | self.mileage 49 | } 50 | 51 | pub fn go(&mut self, mileage: f64) { 52 | if self.started() { 53 | self.mileage += mileage; 54 | } else { 55 | println!("Cannot go(), you must start engine first!"); 56 | } 57 | } 58 | } 59 | 60 | pub struct GpsNavigator { 61 | route: String, 62 | } 63 | 64 | impl GpsNavigator { 65 | pub fn new() -> Self { 66 | Self::from_route( 67 | "221b, Baker Street, London to Scotland Yard, 8-10 Broadway, London".into(), 68 | ) 69 | } 70 | 71 | pub fn from_route(route: String) -> Self { 72 | Self { route } 73 | } 74 | 75 | pub fn route(&self) -> &String { 76 | &self.route 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /creational/builder/director.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | builders::Builder, 3 | components::{CarType, Engine, GpsNavigator, Transmission}, 4 | }; 5 | 6 | /// Director knows how to build a car. 7 | /// 8 | /// However, a builder can build a car manual instead of an actual car, 9 | /// everything depends on the concrete builder. 10 | pub struct Director; 11 | 12 | impl Director { 13 | pub fn construct_sports_car(builder: &mut impl Builder) { 14 | builder.set_car_type(CarType::SportsCar); 15 | builder.set_seats(2); 16 | builder.set_engine(Engine::new(3.0, 0.0)); 17 | builder.set_transmission(Transmission::SemiAutomatic); 18 | builder.set_gsp_navigator(GpsNavigator::new()); 19 | } 20 | 21 | pub fn construct_city_car(builder: &mut impl Builder) { 22 | builder.set_car_type(CarType::CityCar); 23 | builder.set_seats(2); 24 | builder.set_engine(Engine::new(1.2, 0.0)); 25 | builder.set_transmission(Transmission::Automatic); 26 | builder.set_gsp_navigator(GpsNavigator::new()); 27 | } 28 | 29 | pub fn construct_suv(builder: &mut impl Builder) { 30 | builder.set_car_type(CarType::Suv); 31 | builder.set_seats(4); 32 | builder.set_engine(Engine::new(2.5, 0.0)); 33 | builder.set_transmission(Transmission::Manual); 34 | builder.set_gsp_navigator(GpsNavigator::new()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /creational/builder/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | 3 | mod builders; 4 | mod cars; 5 | mod components; 6 | mod director; 7 | 8 | use builders::{Builder, CarBuilder, CarManualBuilder}; 9 | use cars::{Car, Manual}; 10 | use director::Director; 11 | 12 | fn main() { 13 | let mut car_builder = CarBuilder::default(); 14 | 15 | // Director gets the concrete builder object from the client 16 | // (application code). That's because application knows better which 17 | // builder to use to get a specific product. 18 | Director::construct_sports_car(&mut car_builder); 19 | 20 | // The final product is often retrieved from a builder object, since 21 | // Director is not aware and not dependent on concrete builders and 22 | // products. 23 | let car: Car = car_builder.build(); 24 | println!("Car built: {:?}\n", car.car_type()); 25 | 26 | let mut manual_builder = CarManualBuilder::default(); 27 | 28 | // Director may know several building recipes. 29 | Director::construct_city_car(&mut manual_builder); 30 | 31 | // The final car manual. 32 | let manual: Manual = manual_builder.build(); 33 | println!("Car manual built:\n{}", manual); 34 | } 35 | -------------------------------------------------------------------------------- /creational/factory-method/README.md: -------------------------------------------------------------------------------- 1 | # Factory Method 2 | 3 | _**Factory Method** is a creational design pattern that provides an interface 4 | for creating objects in a supertrait, but allows subtraits to alter the type 5 | of objects that will be created._ 6 | 7 | ## Maze Game 8 | 9 | This example reproduces one from the GoF Design Patterns book: 10 | https://en.wikipedia.org/wiki/Factory_method_pattern, implementing 11 | the Factory Method pattern using generics (_static dispatch_). 12 | 13 | ### How to Run 14 | 15 | ```bash 16 | cargo run --bin factory-method-maze-game 17 | ``` 18 | 19 | ### Output 20 | 21 | ``` 22 | Loading resources... 23 | Starting the game... 24 | Magic Room: Infinite Room 25 | Magic Room: Red Room 26 | Loading resources... 27 | Starting the game... 28 | Ordinary Room: #2 29 | Ordinary Room: #1 30 | ``` 31 | 32 | ## Render Dialog 33 | 34 | This example shows a GUI framework can organize its types into 35 | independent modules: 36 | 37 | 1. The `gui` module defines interfaces for all the components. 38 | It has no external dependencies. 39 | 2. The `html_gui` module provides HTML implementation of the base GUI. 40 | Depends on `gui`. 41 | 3. The `windows_gui` module provides Windows implementation of the base GUI. 42 | Depends on `gui`. 43 | 44 | The app is a client application that can use several implementations 45 | of the GUI framework, depending on the current environment or configuration. 46 | However, most of the app code doesn't depend on specific types of GUI elements. 47 | All the client code works with GUI elements through abstract interfaces 48 | defined by the `gui` module. 49 | 50 | 💡 The [Abstract Factory example](../abstract-factory/) demonstrates even 51 | greater separation of Factory interface and its implementations. 52 | 53 | ### How to Run 54 | 55 | ```bash 56 | cargo run --bin factory-method-render-dialog 57 | ``` 58 | 59 | ### Output 60 | 61 | ``` 62 | -- No OS detected, creating the HTML GUI -- 63 | 64 | Click! Button says - 'Hello World!' 65 | Dialog - Refresh 66 | ``` 67 | 68 | ### Reference 69 | 70 | This example reproduces [Factory Method Java Example](https://refactoring.guru/design-patterns/factory-method/java/example). 71 | -------------------------------------------------------------------------------- /creational/factory-method/maze-game/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "maze-game" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "factory-method-maze-game" 8 | path = "main.rs" 9 | -------------------------------------------------------------------------------- /creational/factory-method/maze-game/game.rs: -------------------------------------------------------------------------------- 1 | /// Maze room that is going to be instantiated with a factory method. 2 | pub trait Room { 3 | fn render(&self); 4 | } 5 | 6 | /// Maze game has a factory method producing different rooms. 7 | pub trait MazeGame { 8 | type RoomImpl: Room; 9 | 10 | /// A factory method. 11 | fn rooms(&self) -> Vec; 12 | 13 | fn play(&self) { 14 | for room in self.rooms() { 15 | room.render(); 16 | } 17 | } 18 | } 19 | 20 | /// The client code initializes resources and does other preparations 21 | /// then it uses a factory to construct and run the game. 22 | pub fn run(maze_game: impl MazeGame) { 23 | println!("Loading resources..."); 24 | println!("Starting the game..."); 25 | 26 | maze_game.play(); 27 | } 28 | -------------------------------------------------------------------------------- /creational/factory-method/maze-game/magic_maze.rs: -------------------------------------------------------------------------------- 1 | use super::game::{MazeGame, Room}; 2 | 3 | #[derive(Clone)] 4 | pub struct MagicRoom { 5 | title: String, 6 | } 7 | 8 | impl MagicRoom { 9 | pub fn new(title: String) -> Self { 10 | Self { title } 11 | } 12 | } 13 | 14 | impl Room for MagicRoom { 15 | fn render(&self) { 16 | println!("Magic Room: {}", self.title); 17 | } 18 | } 19 | 20 | pub struct MagicMaze { 21 | rooms: Vec, 22 | } 23 | 24 | impl MagicMaze { 25 | pub fn new() -> Self { 26 | Self { 27 | rooms: vec![ 28 | MagicRoom::new("Infinite Room".into()), 29 | MagicRoom::new("Red Room".into()), 30 | ], 31 | } 32 | } 33 | } 34 | 35 | impl MazeGame for MagicMaze { 36 | type RoomImpl = MagicRoom; 37 | 38 | fn rooms(&self) -> Vec { 39 | self.rooms.clone() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /creational/factory-method/maze-game/main.rs: -------------------------------------------------------------------------------- 1 | mod game; 2 | mod magic_maze; 3 | mod ordinary_maze; 4 | 5 | use magic_maze::MagicMaze; 6 | use ordinary_maze::OrdinaryMaze; 7 | 8 | /// The game runs with different mazes depending on the concrete factory type: 9 | /// it's either an ordinary maze or a magic maze. 10 | /// 11 | /// For demonstration purposes, both mazes are used to construct the game. 12 | fn main() { 13 | // Option 1: The game starts with an ordinary maze. 14 | let ordinary_maze = OrdinaryMaze::new(); 15 | game::run(ordinary_maze); 16 | 17 | // Option 2: The game starts with a magic maze. 18 | let magic_maze = MagicMaze::new(); 19 | game::run(magic_maze); 20 | } 21 | -------------------------------------------------------------------------------- /creational/factory-method/maze-game/ordinary_maze.rs: -------------------------------------------------------------------------------- 1 | use super::game::{MazeGame, Room}; 2 | 3 | #[derive(Clone)] 4 | pub struct OrdinaryRoom { 5 | id: u32, 6 | } 7 | 8 | impl OrdinaryRoom { 9 | pub fn new(id: u32) -> Self { 10 | Self { id } 11 | } 12 | } 13 | 14 | impl Room for OrdinaryRoom { 15 | fn render(&self) { 16 | println!("Ordinary Room: #{}", self.id); 17 | } 18 | } 19 | 20 | pub struct OrdinaryMaze { 21 | rooms: Vec, 22 | } 23 | 24 | impl OrdinaryMaze { 25 | pub fn new() -> Self { 26 | Self { 27 | rooms: vec![OrdinaryRoom::new(1), OrdinaryRoom::new(2)], 28 | } 29 | } 30 | } 31 | 32 | impl MazeGame for OrdinaryMaze { 33 | type RoomImpl = OrdinaryRoom; 34 | 35 | fn rooms(&self) -> Vec { 36 | let mut rooms = self.rooms.clone(); 37 | rooms.reverse(); 38 | rooms 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /creational/factory-method/render-dialog/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "render-dialog" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "factory-method-render-dialog" 8 | path = "main.rs" 9 | -------------------------------------------------------------------------------- /creational/factory-method/render-dialog/gui.rs: -------------------------------------------------------------------------------- 1 | pub trait Button { 2 | fn render(&self); 3 | fn on_click(&self); 4 | } 5 | 6 | /// Dialog has a factory method `create_button`. 7 | /// 8 | /// It creates different buttons depending on a factory implementation. 9 | pub trait Dialog { 10 | /// The factory method. It must be overridden with a concrete implementation. 11 | fn create_button(&self) -> Box; 12 | 13 | fn render(&self) { 14 | let button = self.create_button(); 15 | button.render(); 16 | } 17 | 18 | fn refresh(&self) { 19 | println!("Dialog - Refresh"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /creational/factory-method/render-dialog/html_gui.rs: -------------------------------------------------------------------------------- 1 | use crate::gui::{Button, Dialog}; 2 | 3 | pub struct HtmlButton; 4 | 5 | impl Button for HtmlButton { 6 | fn render(&self) { 7 | println!(""); 8 | self.on_click(); 9 | } 10 | 11 | fn on_click(&self) { 12 | println!("Click! Button says - 'Hello World!'"); 13 | } 14 | } 15 | 16 | pub struct HtmlDialog; 17 | 18 | impl Dialog for HtmlDialog { 19 | /// Creates an HTML button. 20 | fn create_button(&self) -> Box { 21 | Box::new(HtmlButton) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /creational/factory-method/render-dialog/init.rs: -------------------------------------------------------------------------------- 1 | use crate::gui::Dialog; 2 | use crate::html_gui::HtmlDialog; 3 | use crate::windows_gui::WindowsDialog; 4 | 5 | pub fn initialize() -> &'static dyn Dialog { 6 | // The dialog type is selected depending on the environment settings or configuration. 7 | if cfg!(windows) { 8 | println!("-- Windows detected, creating Windows GUI --"); 9 | &WindowsDialog 10 | } else { 11 | println!("-- No OS detected, creating the HTML GUI --"); 12 | &HtmlDialog 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /creational/factory-method/render-dialog/main.rs: -------------------------------------------------------------------------------- 1 | mod gui; 2 | mod html_gui; 3 | mod init; 4 | mod windows_gui; 5 | 6 | use init::initialize; 7 | 8 | fn main() { 9 | // The rest of the code doesn't depend on specific dialog types, because 10 | // it works with all dialog objects via the abstract `Dialog` trait 11 | // which is defined in the `gui` module. 12 | let dialog = initialize(); 13 | dialog.render(); 14 | dialog.refresh(); 15 | } 16 | -------------------------------------------------------------------------------- /creational/factory-method/render-dialog/windows_gui.rs: -------------------------------------------------------------------------------- 1 | use crate::gui::{Button, Dialog}; 2 | 3 | pub struct WindowsButton; 4 | 5 | impl Button for WindowsButton { 6 | fn render(&self) { 7 | println!("Drawing a Windows button"); 8 | self.on_click(); 9 | } 10 | 11 | fn on_click(&self) { 12 | println!("Click! Hello, Windows!"); 13 | } 14 | } 15 | 16 | pub struct WindowsDialog; 17 | 18 | impl Dialog for WindowsDialog { 19 | /// Creates a Windows button. 20 | fn create_button(&self) -> Box { 21 | Box::new(WindowsButton) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /creational/prototype/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "prototype" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "prototype" 8 | path = "main.rs" 9 | -------------------------------------------------------------------------------- /creational/prototype/README.md: -------------------------------------------------------------------------------- 1 | # Prototype 2 | 3 | _**Prototype** allows cloning objects, even complex ones, without coupling to their specific classes._ 4 | 5 | **_Rust_** has standard `Clone` implementation (via `#[derive(Clone)]`) for many 6 | types which makes Prototype easy and seamless to use. 7 | 8 | ```rust 9 | let mut circle2 = circle1.clone(); 10 | ``` 11 | 12 | See **[The Easiest Patterns in Rust](https://fadeevab.com/the-easiest-patterns-in-rust/)**. 13 | 14 | ## How to Execute 15 | 16 | ```bash 17 | cargo run --bin prototype 18 | ``` 19 | 20 | ## Output 21 | 22 | ``` 23 | Circle 1: 10, 15, 10 24 | Circle 2: 10, 15, 77 25 | ``` 26 | -------------------------------------------------------------------------------- /creational/prototype/main.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone)] 2 | struct Circle { 3 | pub x: u32, 4 | pub y: u32, 5 | pub radius: u32, 6 | } 7 | 8 | fn main() { 9 | let circle1 = Circle { 10 | x: 10, 11 | y: 15, 12 | radius: 10, 13 | }; 14 | 15 | // Prototype in action. 16 | let mut circle2 = circle1.clone(); 17 | circle2.radius = 77; 18 | 19 | println!("Circle 1: {}, {}, {}", circle1.x, circle1.y, circle1.radius); 20 | println!("Circle 2: {}, {}, {}", circle2.x, circle2.y, circle2.radius); 21 | } 22 | -------------------------------------------------------------------------------- /creational/simple-factory/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "simple-factory" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "simple-factory" 8 | path = "main.rs" 9 | -------------------------------------------------------------------------------- /creational/simple-factory/README.md: -------------------------------------------------------------------------------- 1 | # Simple Factory 2 | 3 | _The **Simple Factory** pattern is just a function with a large conditional 4 | that based on parameters chooses which product to instantiate and then return._ 5 | 6 | The idea is encapsulating a creation logic inside of a method, while 7 | object utilization is being kept "outside". There is no inheritance and 8 | complex creational traits, thus it's the **Simple** factory. 9 | 10 | Here, `create_button` creates random buttons: 11 | 12 | ```rust 13 | fn create_button(random_number: f64) -> Box { 14 | if random_number < 0.5 { 15 | Box::new(TitleButton::new("Button".to_string())) 16 | } else { 17 | Box::new(IdButton::new(123)) 18 | } 19 | } 20 | ``` 21 | 22 | while `render_dialog` manipulates with whatever it gets from `create_button`: 23 | 24 | ```rust 25 | fn render_dialog(random_number: f64) { 26 | // ... 27 | let button = create_button(random_number); 28 | button.render(); 29 | // ... 30 | } 31 | ``` 32 | 33 | ## How to Run 34 | 35 | ```bash 36 | cargo run --bin simple-factory 37 | ``` 38 | 39 | ## Output 40 | 41 | ``` 42 | -- Title --- 43 | 'Button' 44 | ------------- 45 | --- Title --- 46 | Button #123 47 | ------------- 48 | ``` 49 | -------------------------------------------------------------------------------- /creational/simple-factory/button/id.rs: -------------------------------------------------------------------------------- 1 | use super::Button; 2 | 3 | pub struct IdButton { 4 | id: u32, 5 | } 6 | 7 | impl IdButton { 8 | pub fn new(id: u32) -> Self { 9 | Self { id } 10 | } 11 | } 12 | 13 | impl Button for IdButton { 14 | fn render(&self) { 15 | println!("Button #{}", self.id); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /creational/simple-factory/button/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod id; 2 | pub mod title; 3 | 4 | pub trait Button { 5 | fn render(&self); 6 | } 7 | -------------------------------------------------------------------------------- /creational/simple-factory/button/title.rs: -------------------------------------------------------------------------------- 1 | use super::Button; 2 | 3 | pub struct TitleButton { 4 | title: String, 5 | } 6 | 7 | impl TitleButton { 8 | pub fn new(title: String) -> Self { 9 | Self { title } 10 | } 11 | } 12 | 13 | impl Button for TitleButton { 14 | fn render(&self) { 15 | println!("'{}'", self.title); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /creational/simple-factory/main.rs: -------------------------------------------------------------------------------- 1 | mod button; 2 | 3 | use button::{id::IdButton, title::TitleButton, Button}; 4 | 5 | /// Creates a button depending on a parameter value, it is the simple factory. 6 | fn create_button(random_number: f64) -> Box { 7 | if random_number < 0.5 { 8 | Box::new(TitleButton::new("Button".to_string())) 9 | } else { 10 | Box::new(IdButton::new(123)) 11 | } 12 | } 13 | 14 | fn render_dialog(random_number: f64) { 15 | println!("--- Title ---"); 16 | let button = create_button(random_number); 17 | button.render(); 18 | println!("-------------"); 19 | } 20 | 21 | fn main() { 22 | render_dialog(0.3); 23 | render_dialog(0.6); 24 | } 25 | -------------------------------------------------------------------------------- /creational/singleton/README.md: -------------------------------------------------------------------------------- 1 | # Singleton 2 | 3 | _**Singleton** lets you ensure that only one object of its kind exists, 4 | while providing a global access point to this instance._ 5 | 6 | - [`how-to-create`](./how-to-create/) contains simple examples how to create 7 | an actual singleton object. 8 | - [`logger`](./logger/) is a practical example of how the Rust logging 9 | subsystem is essentially implemented. 10 | -------------------------------------------------------------------------------- /creational/singleton/how-to-create/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "singleton" 4 | version = "0.1.0" 5 | 6 | [dependencies] 7 | lazy_static = "1.4.0" 8 | once_cell = "1.15" 9 | 10 | [[bin]] 11 | name = "singleton-local" 12 | path = "local.rs" 13 | 14 | [[bin]] 15 | name = "singleton-lazy" 16 | path = "lazy.rs" 17 | 18 | [[bin]] 19 | name = "singleton-mutex" 20 | path = "mutex.rs" 21 | 22 | [[bin]] 23 | name = "singleton-once" 24 | path = "once.rs" 25 | -------------------------------------------------------------------------------- /creational/singleton/how-to-create/README.md: -------------------------------------------------------------------------------- 1 | # Singleton 2 | 3 | _**Singleton** lets you ensure that only one object of its kind exists, 4 | while providing a global access point to this instance._ 5 | 6 | Singleton is a _global mutable object_, and in terms of **Rust** 7 | it is a _`static mut` item_ which in turn 8 | [requires an **`unsafe`** block](https://doc.rust-lang.org/reference/items/static-items.html#mutable-statics) 9 | for either reading or writing a mutable static variable. 10 | 11 | For this reason, the Singleton pattern can be perceived as unsafe. However, 12 | the pattern is still widely used in practice. A good read-world example of 13 | Singleton is a `log` crate that introduces `log!`, `debug!` and other logging 14 | macros, which you can use throughout your code after setting up a concrete 15 | logger instance, such as [env_logger](https://crates.io/crates/env_logger). 16 | As we can see, `env_logger` uses 17 | [log::set_boxed_logger](https://docs.rs/log/latest/log/fn.set_boxed_logger.html) 18 | under the hood, which has an unsafe block to set up a global logger object. 19 | 20 | - In order to provide safe and usable access to a singleton object, 21 | introduce an API hiding unsafe blocks under the hood. 22 | - See the thread about a mutable Singleton on Stackoverflow for more information. 23 | 24 | There is a plenty of useful containers that allows to avoid an `unsafe` block 25 | in your code: 26 | 27 | 1. [once_cell::sync::OnceCell](https://docs.rs/once_cell/latest/once_cell/sync/struct.OnceCell.html) 28 | 2. [lazy_static::lazy_static](https://docs.rs/lazy_static/latest/lazy_static) 29 | 3. [std::sync::Mutex](https://doc.rust-lang.org/std/sync/struct.Mutex.html) 30 | 31 | In a general case, you can start with `OnceCell` like in the `once.rs` example 32 | (see below). 33 | 34 | ## `local.rs` 35 | 36 | A safe way to implement Singleton in Rust is using NO global variables 37 | at all and passing everything around through function arguments. 38 | The oldest living variable is an object created at the start of the `main()`. 39 | 40 | ### How to Run 41 | 42 | ```bash 43 | cargo run --bin singleton-local 44 | ``` 45 | 46 | ### Output 47 | 48 | ``` 49 | Final state: 1 50 | ``` 51 | 52 | ## `lazy.rs` 53 | 54 | This is a singleton implementation via `lazy_static!`. 55 | 56 | `lazy-static` allows declaring a static variable with lazy initialization 57 | at first access. A drawback of `lazy_static!` is that it doesn't allow 58 | initialization at the arbitrary code place, only in the static block 59 | with predefined instructions. 60 | 61 | ### How to Run 62 | 63 | ```bash 64 | cargo run --bin singleton-lazy 65 | ``` 66 | 67 | ### Output 68 | 69 | ``` 70 | Called 3 71 | ``` 72 | 73 | ## `once.rs` 74 | 75 | `OnceCell` allows having a custom initialization of a singleton at an 76 | **arbitrary place**, unlike `lazy_static!`, where the initialization must be 77 | placed in a static block. `Mutex` is still needed there to make an actual object 78 | modifiable without an `unsafe` block. 79 | 80 | A [`logger`](../logger/), a practical example of Singleton, is 81 | implemented via `OnceCell`. 82 | 83 | ### How to Run 84 | 85 | ```bash 86 | cargo run --bin singleton-once 87 | ``` 88 | 89 | ### Output 90 | 91 | ``` 92 | [42, 1, 1, 1] 93 | ``` 94 | 95 | ## `mutex.rs` 96 | 97 | ⚠ Starting with `rustc 1.63`. 98 | 99 | > Starting with `Rust 1.63`, it can be easier to work with global mutable 100 | > singletons, although it's still preferable to avoid global variables in most 101 | > cases. 102 | > 103 | > Now that `Mutex::new` is `const`, you can use global static `Mutex` locks 104 | > without needing lazy initialization. 105 | 106 | ```rust 107 | use std::sync::Mutex; 108 | 109 | static GLOBAL_DATA: Mutex> = Mutex::new(Vec::new()); 110 | ``` 111 | 112 | ### How to Run 113 | 114 | ```bash 115 | cargo run --bin singleton-mutex 116 | ``` 117 | 118 | ### Output 119 | 120 | ``` 121 | Called 3 times: [1, 1, 1] 122 | New singleton object: [3, 4, 5] 123 | ``` 124 | -------------------------------------------------------------------------------- /creational/singleton/how-to-create/lazy.rs: -------------------------------------------------------------------------------- 1 | //! Taken from: https://stackoverflow.com/questions/27791532/how-do-i-create-a-global-mutable-singleton 2 | //! 3 | //! Rust doesn't really allow a singleton pattern without `unsafe` because it 4 | //! doesn't have a safe mutable global state. 5 | //! 6 | //! `lazy-static` allows declaring a static variable with lazy initialization 7 | //! at first access. It is actually implemented via `unsafe` with `static mut` 8 | //! manipulation, however, it keeps your code clear of `unsafe` blocks. 9 | //! 10 | //! `Mutex` provides safe access to a single object. 11 | 12 | use lazy_static::lazy_static; 13 | use std::sync::Mutex; 14 | 15 | lazy_static! { 16 | static ref ARRAY: Mutex> = Mutex::new(vec![]); 17 | } 18 | 19 | fn do_a_call() { 20 | ARRAY.lock().unwrap().push(1); 21 | } 22 | 23 | fn main() { 24 | do_a_call(); 25 | do_a_call(); 26 | do_a_call(); 27 | 28 | println!("Called {}", ARRAY.lock().unwrap().len()); 29 | } 30 | -------------------------------------------------------------------------------- /creational/singleton/how-to-create/local.rs: -------------------------------------------------------------------------------- 1 | //! A pure safe way to implement Singleton in Rust is using no static variables 2 | //! and passing everything around through function arguments. 3 | //! The oldest living variable is an object created at the start of the `main()`. 4 | 5 | fn change(global_state: &mut u32) { 6 | *global_state += 1; 7 | } 8 | 9 | fn main() { 10 | let mut global_state = 0u32; 11 | 12 | change(&mut global_state); 13 | 14 | println!("Final state: {}", global_state); 15 | } 16 | -------------------------------------------------------------------------------- /creational/singleton/how-to-create/mutex.rs: -------------------------------------------------------------------------------- 1 | //! ructc 1.63 2 | //! https://stackoverflow.com/questions/27791532/how-do-i-create-a-global-mutable-singleton 3 | //! 4 | //! Starting with Rust 1.63, it can be easier to work with global mutable 5 | //! singletons, although it's still preferable to avoid global variables in most 6 | //! cases. 7 | //! 8 | //! Now that `Mutex::new` is `const`, you can use global static `Mutex` locks 9 | //! without needing lazy initialization. 10 | 11 | use std::sync::Mutex; 12 | 13 | static ARRAY: Mutex> = Mutex::new(Vec::new()); 14 | 15 | fn do_a_call() { 16 | ARRAY.lock().unwrap().push(1); 17 | } 18 | 19 | fn main() { 20 | do_a_call(); 21 | do_a_call(); 22 | do_a_call(); 23 | 24 | let array = ARRAY.lock().unwrap(); 25 | println!("Called {} times: {:?}", array.len(), array); 26 | drop(array); 27 | 28 | *ARRAY.lock().unwrap() = vec![3, 4, 5]; 29 | 30 | println!("New singleton object: {:?}", ARRAY.lock().unwrap()); 31 | } 32 | -------------------------------------------------------------------------------- /creational/singleton/how-to-create/once.rs: -------------------------------------------------------------------------------- 1 | //! `OnceCell` allows having a custom initialization of a singleton at 2 | //! an arbitrary place. The initialization can be done only once. 3 | //! `Mutex` is still needed to make an actual object modifiable 4 | //! without an `unsafe` block. 5 | 6 | use once_cell::sync::OnceCell; 7 | use std::sync::Mutex; 8 | 9 | static ARRAY: OnceCell>> = OnceCell::new(); 10 | 11 | fn singleton_init(array: Vec) { 12 | ARRAY.get_or_init(|| Mutex::new(array)); 13 | } 14 | 15 | fn do_a_call() { 16 | ARRAY.get().unwrap().lock().unwrap().push(1); 17 | } 18 | 19 | fn main() { 20 | singleton_init(vec![42]); 21 | 22 | do_a_call(); 23 | do_a_call(); 24 | do_a_call(); 25 | 26 | println!("{:?}", ARRAY.get().unwrap().lock().unwrap()); 27 | } 28 | -------------------------------------------------------------------------------- /creational/singleton/logger/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "singleton-logger" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "singleton-logger" 8 | path = "main.rs" 9 | 10 | [dependencies] 11 | once_cell = "1.15" -------------------------------------------------------------------------------- /creational/singleton/logger/README.md: -------------------------------------------------------------------------------- 1 | # Logger 2 | 3 | A practical example of how the Rust logging subsystem is essentially implemented using the Singleton pattern. 4 | 5 | - [`log.rs`](./log.rs) - a lightweight logging facade encapsulating a static 6 | logger object (singleton); the module is a representation of 7 | the [`log`](https://docs.rs/log/latest/log/) crate, 8 | - [`simple_logger.rs`](./simple_logger.rs) - a logger implementation printing 9 | into _stdout_, it represents the [`simplelog`](https://docs.rs/simplelog/latest/simplelog/) 10 | crate, 11 | - [`app.rs`](./app.rs) - an arbitrary code using a logging facade, 12 | - [`main.rs`](./main.rs) - application initialization 13 | 14 | ## How to Run 15 | 16 | ```bash 17 | cargo run --bin singleton-logger 18 | ``` 19 | 20 | ## Output 21 | 22 | ```bash 23 | [I] Application starts 24 | [W] Something non-critical happened 25 | [E] Execution failed 26 | ``` 27 | -------------------------------------------------------------------------------- /creational/singleton/logger/app.rs: -------------------------------------------------------------------------------- 1 | use crate::log::{error, info, warn}; 2 | 3 | pub fn run() { 4 | info("Application starts"); 5 | warn("Something non-critical happened"); 6 | error("Execution failed") 7 | } 8 | -------------------------------------------------------------------------------- /creational/singleton/logger/log.rs: -------------------------------------------------------------------------------- 1 | use once_cell::sync::OnceCell; 2 | use std::fmt::Display; 3 | 4 | // OnceCell allows to set a logger later but only once. 5 | static LOGGER: OnceCell> = OnceCell::new(); 6 | 7 | #[derive(Debug)] 8 | pub struct SetLoggerError; 9 | 10 | pub fn set_boxed_logger(logger: Box) -> Result<(), SetLoggerError> { 11 | if LOGGER.set(logger).is_err() { 12 | return Err(SetLoggerError); 13 | } 14 | 15 | Ok(()) 16 | } 17 | 18 | #[derive(PartialEq, Eq, PartialOrd)] 19 | pub enum Level { 20 | Error, 21 | Warn, 22 | Info, 23 | } 24 | 25 | pub trait Log { 26 | fn enabled(&self, level: &Level) -> bool; 27 | fn log(&self, level: &Level, message: &str); 28 | } 29 | 30 | fn log(level: Level, message: &str) { 31 | if let Some(logger) = LOGGER.get() { 32 | if logger.enabled(&level) { 33 | logger.log(&level, message); 34 | } 35 | } 36 | } 37 | 38 | pub fn error(message: &str) { 39 | log(Level::Error, message); 40 | } 41 | 42 | pub fn warn(message: &str) { 43 | log(Level::Warn, message); 44 | } 45 | 46 | pub fn info(message: &str) { 47 | log(Level::Info, message); 48 | } 49 | 50 | impl Display for Level { 51 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 52 | write!( 53 | f, 54 | "{}", 55 | match self { 56 | Level::Error => "E", 57 | Level::Warn => "W", 58 | Level::Info => "I", 59 | } 60 | ) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /creational/singleton/logger/main.rs: -------------------------------------------------------------------------------- 1 | mod app; 2 | mod log; 3 | mod simple_logger; 4 | 5 | use log::{info, Level}; 6 | 7 | fn main() { 8 | info("This log is not going to be displayed"); 9 | 10 | simple_logger::init(Level::Info); 11 | 12 | app::run(); 13 | } 14 | -------------------------------------------------------------------------------- /creational/singleton/logger/simple_logger.rs: -------------------------------------------------------------------------------- 1 | use crate::log::{self, Level, Log}; 2 | 3 | struct SimpleLogger { 4 | max_level: Level, 5 | } 6 | 7 | impl Log for SimpleLogger { 8 | fn enabled(&self, level: &Level) -> bool { 9 | *level <= self.max_level 10 | } 11 | 12 | fn log(&self, level: &Level, message: &str) { 13 | println!("[{}] {}", level, message); 14 | } 15 | } 16 | 17 | pub fn init(max_level: Level) { 18 | log::set_boxed_logger(Box::new(SimpleLogger { max_level })) 19 | .expect("Logger has been already set"); 20 | } 21 | -------------------------------------------------------------------------------- /creational/static-creation-method/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "static-creation-method" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "static-creation-method" 8 | path = "main.rs" 9 | -------------------------------------------------------------------------------- /creational/static-creation-method/README.md: -------------------------------------------------------------------------------- 1 | # Static Creation Method 2 | 3 | _**Static Creation Method** is an 4 | [associated function](https://doc.rust-lang.org/rust-by-example/fn/methods.html) 5 | that returns a new object which is usually an instance of that particular type._ 6 | 7 | There is a notion of "constructor" in a typical OOP language which is a 8 | default class method to create an object. Not in **Rust**: constructors are 9 | _thrown away_ because there is nothing that couldn't be achieved with a static 10 | creation method. 11 | 12 | 💡 See **[The Easiest Patterns in Rust](https://fadeevab.com/the-easiest-patterns-in-rust/)**. 13 | See also [Factory Comparison](https://refactoring.guru/design-patterns/factory-comparison). 14 | 15 | There are a few ways to define a static creation method. 16 | 17 | 1. A `default()` method from Default trait for construction with no parameters. Use either default `#[derive(Default)]`, or a manual trait implementation. 18 | 19 | ```rust 20 | #[derive(Default)] 21 | struct Circle; 22 | 23 | let circle = Circle::default(); 24 | ``` 25 | 26 | 2. A handwritten `new()` method for a custom object creation with parameters: 27 | 28 | ```rust 29 | impl Rectangle { 30 | pub fn new(width: u32, length: u32) -> Rectangle { 31 | Self { width, length } 32 | } 33 | } 34 | 35 | let rectangle = Rectangle::new(10, 20); 36 | ``` 37 | 38 | ### How to Run 39 | 40 | ```bash 41 | cargo run --bin static-creation-method 42 | ``` 43 | 44 | ### Output 45 | 46 | ``` 47 | Alice Fisher 48 | John Smith 49 | ``` 50 | -------------------------------------------------------------------------------- /creational/static-creation-method/main.rs: -------------------------------------------------------------------------------- 1 | struct User { 2 | name: String, 3 | surname: String, 4 | } 5 | 6 | impl User { 7 | // "Static creation method" is actually a Rust's idiomatic way to define a "constructor". 8 | pub fn new(name: String, surname: String) -> Self { 9 | Self { name, surname } 10 | } 11 | 12 | // Constructs an object by reading from database. 13 | pub fn load(id: u32) -> Self { 14 | if id == 42 { 15 | Self { 16 | name: "John".into(), 17 | surname: "Smith".into(), 18 | } 19 | } else { 20 | panic!() 21 | } 22 | } 23 | 24 | pub fn name(&self) -> &String { 25 | &self.name 26 | } 27 | 28 | pub fn surname(&self) -> &String { 29 | &self.surname 30 | } 31 | } 32 | 33 | fn main() { 34 | let alice = User::new("Alice".into(), "Fisher".into()); 35 | let john = User::load(42); 36 | 37 | println!("{} {}", alice.name(), alice.surname()); 38 | println!("{} {}", john.name(), john.surname()); 39 | } 40 | -------------------------------------------------------------------------------- /structural/README.md: -------------------------------------------------------------------------------- 1 | # Structural Patterns 2 | 3 | _**Structural** design patterns explain how to assemble objects and classes into larger structures, while keeping these structures flexible and efficient._ 4 | 5 | 👆 Each example contains its own README and instructions. 6 | -------------------------------------------------------------------------------- /structural/adapter/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "adapter" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "adapter" 8 | path = "main.rs" 9 | -------------------------------------------------------------------------------- /structural/adapter/README.md: -------------------------------------------------------------------------------- 1 | # Adapter 2 | 3 | _**Adapter** is a structural design pattern that allows objects with 4 | incompatible interfaces to collaborate._ 5 | 6 | In this example, the `trait SpecificTarget` is incompatible with a `call` 7 | function which accepts `trait Target` only. 8 | 9 | ```rust 10 | fn call(target: impl Target); 11 | ``` 12 | 13 | The adapter helps to pass the incompatible interface to the `call` function. 14 | 15 | ```rust 16 | let target = TargetAdapter::new(specific_target); 17 | call(target); 18 | ``` 19 | 20 | ## How to Run 21 | 22 | ```bash 23 | cargo run --bin adapter 24 | ``` 25 | 26 | ## Execution Result 27 | 28 | ``` 29 | A compatible target can be directly called: 'Ordinary request.' 30 | Adaptee is incompatible with client: '.tseuqer cificepS' 31 | But with adapter client can call its method: 'Specific request.' 32 | ``` 33 | -------------------------------------------------------------------------------- /structural/adapter/adaptee.rs: -------------------------------------------------------------------------------- 1 | pub struct SpecificTarget; 2 | 3 | impl SpecificTarget { 4 | pub fn specific_request(&self) -> String { 5 | ".tseuqer cificepS".into() 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /structural/adapter/adapter.rs: -------------------------------------------------------------------------------- 1 | use crate::{adaptee::SpecificTarget, Target}; 2 | 3 | /// Converts adaptee's specific interface to a compatible `Target` output. 4 | pub struct TargetAdapter { 5 | adaptee: SpecificTarget, 6 | } 7 | 8 | impl TargetAdapter { 9 | pub fn new(adaptee: SpecificTarget) -> Self { 10 | Self { adaptee } 11 | } 12 | } 13 | 14 | impl Target for TargetAdapter { 15 | fn request(&self) -> String { 16 | // Here's the "adaptation" of a specific output to a compatible output. 17 | self.adaptee.specific_request().chars().rev().collect() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /structural/adapter/main.rs: -------------------------------------------------------------------------------- 1 | mod adaptee; 2 | mod adapter; 3 | mod target; 4 | 5 | use adaptee::SpecificTarget; 6 | use adapter::TargetAdapter; 7 | use target::{OrdinaryTarget, Target}; 8 | 9 | /// Calls any object of a `Target` trait. 10 | /// 11 | /// To understand the Adapter pattern better, imagine that this is 12 | /// a client code, which can operate over a specific interface only 13 | /// (`Target` trait only). It means that an incompatible interface cannot be 14 | /// passed here without an adapter. 15 | fn call(target: impl Target) { 16 | println!("'{}'", target.request()); 17 | } 18 | 19 | fn main() { 20 | let target = OrdinaryTarget; 21 | 22 | print!("A compatible target can be directly called: "); 23 | call(target); 24 | 25 | let adaptee = SpecificTarget; 26 | 27 | println!( 28 | "Adaptee is incompatible with client: '{}'", 29 | adaptee.specific_request() 30 | ); 31 | 32 | let adapter = TargetAdapter::new(adaptee); 33 | 34 | print!("But with adapter client can call its method: "); 35 | call(adapter); 36 | } 37 | -------------------------------------------------------------------------------- /structural/adapter/target.rs: -------------------------------------------------------------------------------- 1 | pub trait Target { 2 | fn request(&self) -> String; 3 | } 4 | 5 | pub struct OrdinaryTarget; 6 | 7 | impl Target for OrdinaryTarget { 8 | fn request(&self) -> String { 9 | "Ordinary request.".into() 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /structural/bridge/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "bridge" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "bridge" 8 | path = "main.rs" 9 | -------------------------------------------------------------------------------- /structural/bridge/README.md: -------------------------------------------------------------------------------- 1 | # Bridge 2 | 3 | _**Bridge** divides business logic or huge type into separate type hierarchies 4 | that can be developed independently._ 5 | 6 | This example reproduces a [Bridge Example in Java](https://refactoring.guru/design-patterns/bridge/java/example). 7 | It shows separation between the types of **remotes** and **devices** 8 | that they control. 9 | 10 | Remotes act as abstractions, and devices are their implementations. 11 | Thanks to the common interfaces, the same remotes can work with different 12 | devices and vice versa. 13 | 14 | The Bridge pattern allows changing or even creating new classes without 15 | touching the code of the opposite hierarchy. 16 | 17 | ## How to Run 18 | 19 | ```bash 20 | cargo run --bin bridge 21 | ``` 22 | 23 | ## Output 24 | 25 | ``` 26 | Tests with basic remote. 27 | Remote: power toggle 28 | ------------------------------------ 29 | | I'm TV set. 30 | | I'm enabled 31 | | Current volume is 30% 32 | | Current channel is 1 33 | ------------------------------------ 34 | 35 | Tests with advanced remote. 36 | Remote: power toggle 37 | Remote: mute 38 | ------------------------------------ 39 | | I'm TV set. 40 | | I'm enabled 41 | | Current volume is 0% 42 | | Current channel is 1 43 | ------------------------------------ 44 | 45 | Tests with basic remote. 46 | Remote: power toggle 47 | ------------------------------------ 48 | | I'm radio. 49 | | I'm enabled 50 | | Current volume is 30% 51 | | Current channel is 1 52 | ------------------------------------ 53 | 54 | Tests with advanced remote. 55 | Remote: power toggle 56 | Remote: mute 57 | ------------------------------------ 58 | | I'm radio. 59 | | I'm enabled 60 | | Current volume is 0% 61 | | Current channel is 1 62 | ------------------------------------ 63 | ``` 64 | -------------------------------------------------------------------------------- /structural/bridge/device/mod.rs: -------------------------------------------------------------------------------- 1 | mod radio; 2 | mod tv; 3 | 4 | pub use radio::Radio; 5 | pub use tv::Tv; 6 | 7 | pub trait Device { 8 | fn is_enabled(&self) -> bool; 9 | fn enable(&mut self); 10 | fn disable(&mut self); 11 | fn volume(&self) -> u8; 12 | fn set_volume(&mut self, percent: u8); 13 | fn channel(&self) -> u16; 14 | fn set_channel(&mut self, channel: u16); 15 | fn print_status(&self); 16 | } 17 | -------------------------------------------------------------------------------- /structural/bridge/device/radio.rs: -------------------------------------------------------------------------------- 1 | use super::Device; 2 | 3 | #[derive(Clone)] 4 | pub struct Radio { 5 | on: bool, 6 | volume: u8, 7 | channel: u16, 8 | } 9 | 10 | impl Default for Radio { 11 | fn default() -> Self { 12 | Self { 13 | on: false, 14 | volume: 30, 15 | channel: 1, 16 | } 17 | } 18 | } 19 | 20 | impl Device for Radio { 21 | fn is_enabled(&self) -> bool { 22 | self.on 23 | } 24 | 25 | fn enable(&mut self) { 26 | self.on = true; 27 | } 28 | 29 | fn disable(&mut self) { 30 | self.on = false; 31 | } 32 | 33 | fn volume(&self) -> u8 { 34 | self.volume 35 | } 36 | 37 | fn set_volume(&mut self, percent: u8) { 38 | self.volume = std::cmp::min(percent, 100); 39 | } 40 | 41 | fn channel(&self) -> u16 { 42 | self.channel 43 | } 44 | 45 | fn set_channel(&mut self, channel: u16) { 46 | self.channel = channel; 47 | } 48 | 49 | fn print_status(&self) { 50 | println!("------------------------------------"); 51 | println!("| I'm radio."); 52 | println!("| I'm {}", if self.on { "enabled" } else { "disabled" }); 53 | println!("| Current volume is {}%", self.volume); 54 | println!("| Current channel is {}", self.channel); 55 | println!("------------------------------------\n"); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /structural/bridge/device/tv.rs: -------------------------------------------------------------------------------- 1 | use super::Device; 2 | 3 | #[derive(Clone)] 4 | pub struct Tv { 5 | on: bool, 6 | volume: u8, 7 | channel: u16, 8 | } 9 | 10 | impl Default for Tv { 11 | fn default() -> Self { 12 | Self { 13 | on: false, 14 | volume: 30, 15 | channel: 1, 16 | } 17 | } 18 | } 19 | 20 | impl Device for Tv { 21 | fn is_enabled(&self) -> bool { 22 | self.on 23 | } 24 | 25 | fn enable(&mut self) { 26 | self.on = true; 27 | } 28 | 29 | fn disable(&mut self) { 30 | self.on = false; 31 | } 32 | 33 | fn volume(&self) -> u8 { 34 | self.volume 35 | } 36 | 37 | fn set_volume(&mut self, percent: u8) { 38 | self.volume = std::cmp::min(percent, 100); 39 | } 40 | 41 | fn channel(&self) -> u16 { 42 | self.channel 43 | } 44 | 45 | fn set_channel(&mut self, channel: u16) { 46 | self.channel = channel; 47 | } 48 | 49 | fn print_status(&self) { 50 | println!("------------------------------------"); 51 | println!("| I'm TV set."); 52 | println!("| I'm {}", if self.on { "enabled" } else { "disabled" }); 53 | println!("| Current volume is {}%", self.volume); 54 | println!("| Current channel is {}", self.channel); 55 | println!("------------------------------------\n"); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /structural/bridge/main.rs: -------------------------------------------------------------------------------- 1 | mod device; 2 | mod remotes; 3 | 4 | use device::{Device, Radio, Tv}; 5 | use remotes::{AdvancedRemote, BasicRemote, HasMutableDevice, Remote}; 6 | 7 | fn main() { 8 | test_device(Tv::default()); 9 | test_device(Radio::default()); 10 | } 11 | 12 | fn test_device(device: impl Device + Clone) { 13 | println!("Tests with basic remote."); 14 | let mut basic_remote = BasicRemote::new(device.clone()); 15 | basic_remote.power(); 16 | basic_remote.device().print_status(); 17 | 18 | println!("Tests with advanced remote."); 19 | let mut advanced_remote = AdvancedRemote::new(device); 20 | advanced_remote.power(); 21 | advanced_remote.mute(); 22 | advanced_remote.device().print_status(); 23 | } 24 | -------------------------------------------------------------------------------- /structural/bridge/remotes/advanced.rs: -------------------------------------------------------------------------------- 1 | use crate::device::Device; 2 | 3 | use super::{HasMutableDevice, Remote}; 4 | 5 | pub struct AdvancedRemote { 6 | device: D, 7 | } 8 | 9 | impl AdvancedRemote { 10 | pub fn new(device: D) -> Self { 11 | Self { device } 12 | } 13 | 14 | pub fn mute(&mut self) { 15 | println!("Remote: mute"); 16 | self.device.set_volume(0); 17 | } 18 | } 19 | 20 | impl HasMutableDevice for AdvancedRemote { 21 | fn device(&mut self) -> &mut D { 22 | &mut self.device 23 | } 24 | } 25 | 26 | impl Remote for AdvancedRemote {} 27 | -------------------------------------------------------------------------------- /structural/bridge/remotes/basic.rs: -------------------------------------------------------------------------------- 1 | use crate::device::Device; 2 | 3 | use super::{HasMutableDevice, Remote}; 4 | 5 | pub struct BasicRemote { 6 | device: D, 7 | } 8 | 9 | impl BasicRemote { 10 | pub fn new(device: D) -> Self { 11 | Self { device } 12 | } 13 | } 14 | 15 | impl HasMutableDevice for BasicRemote { 16 | fn device(&mut self) -> &mut D { 17 | &mut self.device 18 | } 19 | } 20 | 21 | impl Remote for BasicRemote {} 22 | -------------------------------------------------------------------------------- /structural/bridge/remotes/mod.rs: -------------------------------------------------------------------------------- 1 | mod advanced; 2 | mod basic; 3 | 4 | pub use advanced::AdvancedRemote; 5 | pub use basic::BasicRemote; 6 | 7 | use crate::device::Device; 8 | 9 | pub trait HasMutableDevice { 10 | fn device(&mut self) -> &mut D; 11 | } 12 | 13 | pub trait Remote: HasMutableDevice { 14 | fn power(&mut self) { 15 | println!("Remote: power toggle"); 16 | if self.device().is_enabled() { 17 | self.device().disable(); 18 | } else { 19 | self.device().enable(); 20 | } 21 | } 22 | 23 | fn volume_down(&mut self) { 24 | println!("Remote: volume down"); 25 | let volume = self.device().volume(); 26 | self.device().set_volume(volume - 10); 27 | } 28 | 29 | fn volume_up(&mut self) { 30 | println!("Remote: volume up"); 31 | let volume = self.device().volume(); 32 | self.device().set_volume(volume + 10); 33 | } 34 | 35 | fn channel_down(&mut self) { 36 | println!("Remote: channel down"); 37 | let channel = self.device().channel(); 38 | self.device().set_channel(channel - 1); 39 | } 40 | 41 | fn channel_up(&mut self) { 42 | println!("Remote: channel up"); 43 | let channel = self.device().channel(); 44 | self.device().set_channel(channel + 1); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /structural/composite/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "composite" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "composite" 8 | path = "main.rs" 9 | -------------------------------------------------------------------------------- /structural/composite/README.md: -------------------------------------------------------------------------------- 1 | # Composite 2 | 3 | _**Composite** is a structural design pattern that allows composing objects 4 | into a tree-like structure and work with the it as if it was a singular object._ 5 | 6 | Let’s try to understand the Composite pattern with an example of an operating 7 | system’s file system. In the file system, there are two types of objects: 8 | files and folders. There are cases when files and folders should be treated 9 | to be the same way. This is where the Composite pattern comes in handy. 10 | 11 | `File` and `Directory` are both of the `trait Component` with a single `search` 12 | method. For a file, it will just look into the contents of the file; 13 | for a folder, it will go through all files of that folder to find that keyword. 14 | 15 | ## How to Run 16 | 17 | ```bash 18 | cargo run --bin composite 19 | ``` 20 | 21 | ## Execution Result 22 | 23 | ``` 24 | Searching recursively for keyword rose in folder Folder 2 25 | Searching for keyword rose in file File 2 26 | Searching for keyword rose in file File 3 27 | Searching recursively for keyword rose in folder Folder 1 28 | Searching for keyword rose in file File 1 29 | ``` 30 | 31 | ## Reference 32 | 33 | This example replicates the [Composite Example in Go](https://refactoring.guru/design-patterns/composite/go/example). -------------------------------------------------------------------------------- /structural/composite/fs/file.rs: -------------------------------------------------------------------------------- 1 | use super::Component; 2 | 3 | pub struct File { 4 | name: &'static str, 5 | } 6 | 7 | impl File { 8 | pub fn new(name: &'static str) -> Self { 9 | Self { name } 10 | } 11 | } 12 | 13 | impl Component for File { 14 | fn search(&self, keyword: &str) { 15 | println!("Searching for keyword {} in file {}", keyword, self.name); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /structural/composite/fs/folder.rs: -------------------------------------------------------------------------------- 1 | use super::Component; 2 | 3 | pub struct Folder { 4 | name: &'static str, 5 | components: Vec>, 6 | } 7 | 8 | impl Folder { 9 | pub fn new(name: &'static str) -> Self { 10 | Self { 11 | name, 12 | components: vec![], 13 | } 14 | } 15 | 16 | pub fn add(&mut self, component: impl Component + 'static) { 17 | self.components.push(Box::new(component)); 18 | } 19 | } 20 | 21 | impl Component for Folder { 22 | fn search(&self, keyword: &str) { 23 | println!( 24 | "Searching recursively for keyword {} in folder {}", 25 | keyword, self.name 26 | ); 27 | 28 | for component in self.components.iter() { 29 | component.search(keyword); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /structural/composite/fs/mod.rs: -------------------------------------------------------------------------------- 1 | mod file; 2 | mod folder; 3 | 4 | pub use file::File; 5 | pub use folder::Folder; 6 | 7 | pub trait Component { 8 | fn search(&self, keyword: &str); 9 | } 10 | -------------------------------------------------------------------------------- /structural/composite/main.rs: -------------------------------------------------------------------------------- 1 | mod fs; 2 | 3 | use fs::{Component, File, Folder}; 4 | 5 | fn main() { 6 | let file1 = File::new("File 1"); 7 | let file2 = File::new("File 2"); 8 | let file3 = File::new("File 3"); 9 | 10 | let mut folder1 = Folder::new("Folder 1"); 11 | folder1.add(file1); 12 | 13 | let mut folder2 = Folder::new("Folder 2"); 14 | folder2.add(file2); 15 | folder2.add(file3); 16 | folder2.add(folder1); 17 | 18 | folder2.search("rose"); 19 | } 20 | -------------------------------------------------------------------------------- /structural/decorator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "decorator" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "decorator" 8 | path = "main.rs" 9 | -------------------------------------------------------------------------------- /structural/decorator/README.md: -------------------------------------------------------------------------------- 1 | # Decorator 2 | 3 | _**Decorator** is a structural pattern that allows adding new behaviors 4 | to objects by placing them inside special wrapper objects, called decorators._ 5 | 6 | There is a **_practical example_** in Rust's standard library for input/output 7 | operations. 8 | 9 | A buffered reader decorates a vector reader adding buffered behavior. 10 | 11 | ```rust 12 | let mut input = BufReader::new(Cursor::new("Input data")); 13 | input.read(&mut buf).ok(); 14 | ``` 15 | 16 | ## How to Run 17 | 18 | ```bash 19 | cargo run --bin decorator 20 | ``` 21 | 22 | ## Output 23 | 24 | ``` 25 | Read from a buffered reader: Input data 26 | ``` 27 | -------------------------------------------------------------------------------- /structural/decorator/main.rs: -------------------------------------------------------------------------------- 1 | use std::io::{BufReader, Cursor, Read}; 2 | 3 | fn main() { 4 | let mut buf = [0u8; 10]; 5 | 6 | // A buffered reader decorates a vector reader which wraps input data. 7 | let mut input = BufReader::new(Cursor::new("Input data")); 8 | 9 | input.read(&mut buf).ok(); 10 | 11 | print!("Read from a buffered reader: "); 12 | 13 | for byte in buf { 14 | print!("{}", char::from(byte)); 15 | } 16 | 17 | println!(); 18 | } 19 | -------------------------------------------------------------------------------- /structural/facade/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "facade" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | [[bin]] 11 | name = "facade" 12 | path = "main.rs" 13 | -------------------------------------------------------------------------------- /structural/facade/README.md: -------------------------------------------------------------------------------- 1 | # Facade 2 | 3 | _**Facade** is a structural design pattern that provides a simplified 4 | (but limited) interface to a complex system of classes, library or framework._ 5 | 6 | ## Conceptual Example 7 | 8 | `pub struct WalletFacade` hides a complex logic behind its API. A single method 9 | `add_money_to_wallet` interacts with the account, code, wallet, notification 10 | and ledger behind the scenes. 11 | 12 | ## How to Run 13 | 14 | ```bash 15 | cargo run --bin facade 16 | ``` 17 | 18 | ## Execution Result 19 | 20 | ``` 21 | Starting create account 22 | Account created 23 | 24 | Starting add money to wallet 25 | Account verified 26 | Security code verified 27 | Sending wallet credit notification 28 | Make ledger entry for accountId abc with transaction type credit for amount 10 29 | 30 | Starting debit money from wallet 31 | Account verified 32 | Security code verified 33 | Sending wallet debit notification 34 | Make ledger entry for accountId abc with transaction type debit for amount 5 35 | ``` 36 | 37 | ## Reference 38 | 39 | This examples reproduces the [Facade Example in Go](https://refactoring.guru/design-patterns/facade/go/example). 40 | -------------------------------------------------------------------------------- /structural/facade/account.rs: -------------------------------------------------------------------------------- 1 | pub struct Account { 2 | name: String, 3 | } 4 | 5 | impl Account { 6 | pub fn new(name: String) -> Self { 7 | Self { name } 8 | } 9 | 10 | pub fn check(&self, name: &String) -> Result<(), String> { 11 | if &self.name != name { 12 | return Err("Account name is incorrect".into()); 13 | } 14 | 15 | println!("Account verified"); 16 | Ok(()) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /structural/facade/ledger.rs: -------------------------------------------------------------------------------- 1 | pub struct Ledger; 2 | 3 | impl Ledger { 4 | pub fn make_entry(&mut self, account_id: &String, txn_type: String, amount: u32) { 5 | println!( 6 | "Make ledger entry for accountId {} with transaction type {} for amount {}", 7 | account_id, txn_type, amount 8 | ); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /structural/facade/main.rs: -------------------------------------------------------------------------------- 1 | mod account; 2 | mod ledger; 3 | mod notification; 4 | mod security_code; 5 | mod wallet; 6 | mod wallet_facade; 7 | 8 | use wallet_facade::WalletFacade; 9 | 10 | fn main() -> Result<(), String> { 11 | let mut wallet = WalletFacade::new("abc".into(), 1234); 12 | println!(); 13 | 14 | // Wallet Facade interacts with the account, code, wallet, notification and 15 | // ledger behind the scenes. 16 | wallet.add_money_to_wallet(&"abc".into(), 1234, 10)?; 17 | println!(); 18 | 19 | wallet.deduct_money_from_wallet(&"abc".into(), 1234, 5) 20 | } 21 | -------------------------------------------------------------------------------- /structural/facade/notification.rs: -------------------------------------------------------------------------------- 1 | pub struct Notification; 2 | 3 | impl Notification { 4 | pub fn send_wallet_credit_notification(&self) { 5 | println!("Sending wallet credit notification"); 6 | } 7 | 8 | pub fn send_wallet_debit_notification(&self) { 9 | println!("Sending wallet debit notification"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /structural/facade/security_code.rs: -------------------------------------------------------------------------------- 1 | pub struct SecurityCode { 2 | code: u32, 3 | } 4 | 5 | impl SecurityCode { 6 | pub fn new(code: u32) -> Self { 7 | Self { code } 8 | } 9 | 10 | pub fn check(&self, code: u32) -> Result<(), String> { 11 | if self.code != code { 12 | return Err("Security code is incorrect".into()); 13 | } 14 | 15 | println!("Security code verified"); 16 | Ok(()) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /structural/facade/wallet.rs: -------------------------------------------------------------------------------- 1 | pub struct Wallet { 2 | balance: u32, 3 | } 4 | 5 | impl Wallet { 6 | pub fn new() -> Self { 7 | Self { balance: 0 } 8 | } 9 | 10 | pub fn credit_balance(&mut self, amount: u32) { 11 | self.balance += amount; 12 | } 13 | 14 | pub fn debit_balance(&mut self, amount: u32) { 15 | self.balance 16 | .checked_sub(amount) 17 | .expect("Balance is not sufficient"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /structural/facade/wallet_facade.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | account::Account, ledger::Ledger, notification::Notification, security_code::SecurityCode, 3 | wallet::Wallet, 4 | }; 5 | 6 | /// Facade hides a complex logic behind the API. 7 | pub struct WalletFacade { 8 | account: Account, 9 | wallet: Wallet, 10 | code: SecurityCode, 11 | notification: Notification, 12 | ledger: Ledger, 13 | } 14 | 15 | impl WalletFacade { 16 | pub fn new(account_id: String, code: u32) -> Self { 17 | println!("Starting create account"); 18 | 19 | let this = Self { 20 | account: Account::new(account_id), 21 | wallet: Wallet::new(), 22 | code: SecurityCode::new(code), 23 | notification: Notification, 24 | ledger: Ledger, 25 | }; 26 | 27 | println!("Account created"); 28 | this 29 | } 30 | 31 | pub fn add_money_to_wallet( 32 | &mut self, 33 | account_id: &String, 34 | security_code: u32, 35 | amount: u32, 36 | ) -> Result<(), String> { 37 | println!("Starting add money to wallet"); 38 | self.account.check(account_id)?; 39 | self.code.check(security_code)?; 40 | self.wallet.credit_balance(amount); 41 | self.notification.send_wallet_credit_notification(); 42 | self.ledger.make_entry(account_id, "credit".into(), amount); 43 | Ok(()) 44 | } 45 | 46 | pub fn deduct_money_from_wallet( 47 | &mut self, 48 | account_id: &String, 49 | security_code: u32, 50 | amount: u32, 51 | ) -> Result<(), String> { 52 | println!("Starting debit money from wallet"); 53 | self.account.check(account_id)?; 54 | self.code.check(security_code)?; 55 | self.wallet.debit_balance(amount); 56 | self.notification.send_wallet_debit_notification(); 57 | self.ledger.make_entry(account_id, "debit".into(), amount); 58 | Ok(()) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /structural/flyweight/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "flyweight" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "flyweight" 8 | path = "main.rs" 9 | 10 | [dependencies] 11 | draw = "0.3" 12 | memory-stats = "1.0.0" 13 | rand = "0.8" 14 | -------------------------------------------------------------------------------- /structural/flyweight/README.md: -------------------------------------------------------------------------------- 1 | # Flyweight 2 | 3 | _**Flyweight** (Cache) is a structural design pattern that allows programs to 4 | support vast quantities of objects by keeping their memory consumption low._ 5 | 6 | It's an _internal cache_ hidden behind a settle Facade-like API. 7 | The cache stores shared parts that are referenced from multiple objects. 8 | _See more explanation below._ 9 | 10 | 11 | ## Rendering a Forest 12 | 13 | ```bash 14 | cargo run --release 15 | ``` 16 | 17 | ## Screenshot 18 | 19 | _res/forest.svg_ (10,000 trees): 20 | 21 | ![Rendered Forest](res/forest.svg) 22 | 23 | ## RAM usage stats 24 | 25 | For 100,000 trees: 26 | 27 | ``` 28 | 100000 trees drawn 29 | Cache length: 2 tree kinds 30 | ------------------------------- 31 | Memory usage: 32 | Tree size (16 bytes) * 100000 33 | + TreeKind size (~30 bytes) * 2 34 | ------------------------------- 35 | Total: 1688KB (estimated 1562KB), 36 | instead of 4492KB 37 | ``` 38 | 39 | ## Overview 40 | 41 | `Forest` has a public API that can not be changed for backward compatibility reasons: 42 | 43 | ```rust 44 | pub fn plant_tree(&mut self, x: u32, y: u32, color: TreeColor, name: String, data: String); 45 | ``` 46 | 47 | There is an internal cache that is implemented via a `HashSet` that holds 48 | only one copy of a common part (`TreeKind`) of thousands of trees. 49 | 50 | ```rust 51 | #[derive(Default)] 52 | pub struct Forest { 53 | cache: HashSet>, 54 | trees: Vec, 55 | } 56 | ``` 57 | 58 | The point is having an opaque cache implementation. It can use a hash set, 59 | FIFO, or even a simple vector. And it's hidden behind the API because that's 60 | the point: we try to optimize internals without changing a public method, 61 | otherwise we could always pass a common part from top to bottom. 62 | 63 | Other points: 64 | - `cache` is of `HashSet` type, so it can hold only a single 65 | instance of a `TreeKind`, 66 | - `Rc` is needed to get the reference on the tree kind without 67 | cloning a full structure, 68 | - `TreeKind` must derive `Eq`, `PartialEq`, and `Hash` traits to be 69 | used in the `HashSet`. 70 | 71 | ## Reference 72 | 73 | The example reproduces a [Flyweight Example in Java (Rendering a Forest)](https://refactoring.guru/design-patterns/flyweight/java/example). 74 | -------------------------------------------------------------------------------- /structural/flyweight/forest.rs: -------------------------------------------------------------------------------- 1 | mod tree; 2 | 3 | use draw::Canvas; 4 | use std::{collections::HashSet, rc::Rc}; 5 | use tree::{Tree, TreeKind}; 6 | 7 | pub use self::tree::TreeColor; 8 | 9 | /// Forest implements an internal cache that is hidden behind the public API. 10 | /// 11 | /// The point is having an opaque cache implementation. It can use a hash set, 12 | /// FIFO, or even a simple vector. 13 | /// 14 | /// Here are the key points: 15 | /// - `cache` is of `HashSet` type, so it can hold only a single 16 | /// instance of a `TreeKind`, 17 | /// - `Rc` is needed to get the reference on the tree kind without 18 | /// cloning the full structure, 19 | /// - `TreeKind` must derive `Eq`, `PartialEq`, and `Hash` traits to be 20 | /// used in the `HashSet`. 21 | #[derive(Default)] 22 | pub struct Forest { 23 | cache: HashSet>, 24 | trees: Vec, 25 | } 26 | 27 | impl Forest { 28 | pub fn plant_tree(&mut self, x: u32, y: u32, color: TreeColor, name: String, data: String) { 29 | let tree_kind = TreeKind::new(color, name, data); 30 | 31 | // Here is an essence of Flyweight: it's an internal cache, 32 | // there is always a single instance of a "tree kind" structure. 33 | self.cache.insert(Rc::new(tree_kind.clone())); 34 | 35 | // A tree kind is referenced from each tree instance using `Rc` pointer. 36 | // `tree_kind.clone()` increases a reference counter instead of real cloning. 37 | let tree = Tree::new(x, y, self.cache.get(&tree_kind).unwrap().clone()); 38 | self.trees.push(tree); 39 | } 40 | 41 | pub fn draw(&self, canvas: &mut Canvas) { 42 | for tree in &self.trees { 43 | tree.draw(canvas); 44 | } 45 | } 46 | 47 | pub fn cache_len(&self) -> usize { 48 | self.cache.len() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /structural/flyweight/forest/tree.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use draw::{Canvas, Drawing, Shape, Style, RGB}; 4 | 5 | #[derive(Clone, PartialEq, Eq, Hash)] 6 | pub enum TreeColor { 7 | Color1, 8 | Color2, 9 | TrunkColor, 10 | } 11 | 12 | impl TreeColor { 13 | fn rgb(&self) -> RGB { 14 | match self { 15 | Self::Color1 => RGB::new(0x17, 0xd7, 0xa0), 16 | Self::Color2 => RGB::new(0xd8, 0x21, 0x48), 17 | Self::TrunkColor => RGB::new(0x15, 0x1d, 0x3b), 18 | } 19 | } 20 | } 21 | 22 | /// A cacheable item. It derives `PartialEq`, `Eq`, and `Hash` in order to be 23 | /// used in the `HashSet`. 24 | #[derive(Clone, PartialEq, Eq, Hash)] 25 | pub struct TreeKind { 26 | color: TreeColor, 27 | _name: String, 28 | _data: String, 29 | } 30 | 31 | impl TreeKind { 32 | pub fn new(color: TreeColor, _name: String, _data: String) -> Self { 33 | Self { 34 | color, 35 | _name, 36 | _data, 37 | } 38 | } 39 | 40 | pub fn draw(&self, canvas: &mut Canvas, x: u32, y: u32) { 41 | let rect = Drawing::new() 42 | .with_xy(x.saturating_sub(2) as f32, y as f32) 43 | .with_shape(Shape::Rectangle { 44 | width: 4, 45 | height: 5, 46 | }) 47 | .with_style(Style::filled(TreeColor::TrunkColor.rgb())); 48 | 49 | let circle = Drawing::new() 50 | .with_xy(x as f32, y.saturating_sub(5) as f32) 51 | .with_shape(Shape::Circle { radius: 5 }) 52 | .with_style(Style::filled(self.color.rgb())); 53 | 54 | canvas.display_list.add(rect); 55 | canvas.display_list.add(circle); 56 | } 57 | } 58 | 59 | pub struct Tree { 60 | x: u32, 61 | y: u32, 62 | kind: Rc, 63 | } 64 | 65 | impl Tree { 66 | pub fn new(x: u32, y: u32, kind: Rc) -> Self { 67 | Self { x, y, kind } 68 | } 69 | 70 | pub fn draw(&self, canvas: &mut Canvas) { 71 | self.kind.draw(canvas, self.x, self.y); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /structural/flyweight/main.rs: -------------------------------------------------------------------------------- 1 | mod forest; 2 | 3 | use draw::{render, render::svg::SvgRenderer, Canvas}; 4 | use rand::Rng; 5 | 6 | use crate::forest::{Forest, TreeColor}; 7 | 8 | const CANVAS_SIZE: u32 = 500; 9 | const TREES_TO_DRAW: u32 = 100000; 10 | const TREE_TYPES: u32 = 2; 11 | 12 | fn main() { 13 | let forest = &mut Forest::default(); 14 | 15 | let phys_mem_before = memory_stats::memory_stats().unwrap().physical_mem; 16 | 17 | for _ in 0..TREES_TO_DRAW / TREE_TYPES { 18 | let mut rng = rand::thread_rng(); 19 | 20 | forest.plant_tree( 21 | rng.gen_range(0..CANVAS_SIZE), 22 | rng.gen_range(0..CANVAS_SIZE), 23 | TreeColor::Color1, 24 | "Summer Oak".into(), 25 | "Oak texture stub".into(), 26 | ); 27 | 28 | forest.plant_tree( 29 | rng.gen_range(0..CANVAS_SIZE), 30 | rng.gen_range(0..CANVAS_SIZE), 31 | TreeColor::Color2, 32 | "Autumn Oak".into(), 33 | "Autumn Oak texture stub".into(), 34 | ); 35 | } 36 | 37 | let phys_mem_after = memory_stats::memory_stats().unwrap().physical_mem; 38 | 39 | let mut canvas = Canvas::new(CANVAS_SIZE, CANVAS_SIZE); 40 | forest.draw(&mut canvas); 41 | 42 | render::save(&canvas, "res/forest.svg", SvgRenderer::new()).expect("Rendering"); 43 | 44 | println!("{} trees drawn", TREES_TO_DRAW); 45 | println!("Cache length: {} tree kinds", forest.cache_len()); 46 | println!("-------------------------------"); 47 | println!("Memory usage:"); 48 | println!("Tree size (16 bytes) * {}", TREES_TO_DRAW); 49 | println!("+ TreeKind size (~30 bytes) * {}", TREE_TYPES); 50 | println!("-------------------------------"); 51 | println!( 52 | "Total: {}KB (estimated {}KB),\n instead of {}KB", 53 | (phys_mem_after - phys_mem_before) / 1024, 54 | (TREES_TO_DRAW * 16 + TREE_TYPES * 30) / 1024, 55 | ((TREES_TO_DRAW * 46) / 1024) 56 | ); 57 | } 58 | -------------------------------------------------------------------------------- /structural/proxy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "proxy" 4 | version = "0.1.0" 5 | 6 | [[bin]] 7 | name = "proxy" 8 | path = "main.rs" 9 | -------------------------------------------------------------------------------- /structural/proxy/README.md: -------------------------------------------------------------------------------- 1 | # Proxy 2 | 3 | _**Proxy** is a structural design pattern that provides an object that acts as a 4 | substitute for a real service object used by a client. A proxy receives client 5 | requests, does some work (**access control, caching**, etc.) and then passes the 6 | request to a service object._ 7 | 8 | ## Conceptual Example: Nginx Proxy 9 | 10 | A web server such as Nginx can act as a proxy for your application server: 11 | 12 | - It provides controlled access to your application server. 13 | - It can do rate limiting. 14 | - It can do request caching. 15 | 16 | ## How to Run 17 | 18 | ```bash 19 | cargo run --bin proxy 20 | ``` 21 | 22 | ## Execution Result 23 | 24 | ``` 25 | Url: /app/status 26 | HttpCode: 200 27 | Body: Ok 28 | 29 | Url: /app/status 30 | HttpCode: 200 31 | Body: Ok 32 | 33 | Url: /app/status 34 | HttpCode: 403 35 | Body: Not Allowed 36 | 37 | Url: /create/user 38 | HttpCode: 201 39 | Body: User Created 40 | 41 | Url: /create/user 42 | HttpCode: 404 43 | Body: Not Ok 44 | ``` 45 | 46 | ## Reference 47 | 48 | This example reproduces a [Proxy Example in Go](https://refactoring.guru/design-patterns/proxy/go/example). 49 | -------------------------------------------------------------------------------- /structural/proxy/main.rs: -------------------------------------------------------------------------------- 1 | mod server; 2 | 3 | use crate::server::{NginxServer, Server}; 4 | 5 | fn main() { 6 | let app_status = &"/app/status".to_string(); 7 | let create_user = &"/create/user".to_string(); 8 | 9 | let mut nginx = NginxServer::new(); 10 | 11 | let (code, body) = nginx.handle_request(app_status, "GET"); 12 | println!("Url: {}\nHttpCode: {}\nBody: {}\n", app_status, code, body); 13 | 14 | let (code, body) = nginx.handle_request(app_status, "GET"); 15 | println!("Url: {}\nHttpCode: {}\nBody: {}\n", app_status, code, body); 16 | 17 | let (code, body) = nginx.handle_request(app_status, "GET"); 18 | println!("Url: {}\nHttpCode: {}\nBody: {}\n", app_status, code, body); 19 | 20 | let (code, body) = nginx.handle_request(create_user, "POST"); 21 | println!("Url: {}\nHttpCode: {}\nBody: {}\n", create_user, code, body); 22 | 23 | let (code, body) = nginx.handle_request(create_user, "GET"); 24 | println!("Url: {}\nHttpCode: {}\nBody: {}\n", create_user, code, body); 25 | } 26 | -------------------------------------------------------------------------------- /structural/proxy/server.rs: -------------------------------------------------------------------------------- 1 | mod application; 2 | mod nginx; 3 | 4 | pub use nginx::NginxServer; 5 | 6 | pub trait Server { 7 | fn handle_request(&mut self, url: &str, method: &str) -> (u16, String); 8 | } 9 | -------------------------------------------------------------------------------- /structural/proxy/server/application.rs: -------------------------------------------------------------------------------- 1 | use super::Server; 2 | 3 | pub struct Application; 4 | 5 | impl Server for Application { 6 | fn handle_request(&mut self, url: &str, method: &str) -> (u16, String) { 7 | if url == "/app/status" && method == "GET" { 8 | return (200, "Ok".into()); 9 | } 10 | 11 | if url == "/create/user" && method == "POST" { 12 | return (201, "User Created".into()); 13 | } 14 | 15 | (404, "Not Ok".into()) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /structural/proxy/server/nginx.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use super::{application::Application, Server}; 4 | 5 | /// NGINX server is a proxy to an application server. 6 | pub struct NginxServer { 7 | application: Application, 8 | max_allowed_requests: u32, 9 | rate_limiter: HashMap, 10 | } 11 | 12 | impl NginxServer { 13 | pub fn new() -> Self { 14 | Self { 15 | application: Application, 16 | max_allowed_requests: 2, 17 | rate_limiter: HashMap::default(), 18 | } 19 | } 20 | 21 | pub fn check_rate_limiting(&mut self, url: &str) -> bool { 22 | let rate = self.rate_limiter.entry(url.to_string()).or_insert(1); 23 | 24 | if *rate > self.max_allowed_requests { 25 | return false; 26 | } 27 | 28 | *rate += 1; 29 | true 30 | } 31 | } 32 | 33 | impl Server for NginxServer { 34 | fn handle_request(&mut self, url: &str, method: &str) -> (u16, String) { 35 | if !self.check_rate_limiting(url) { 36 | return (403, "Not Allowed".into()); 37 | } 38 | 39 | self.application.handle_request(url, method) 40 | } 41 | } 42 | --------------------------------------------------------------------------------