├── .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 | [](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 |
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 | 
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 | |  |  |
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 | 
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 | 
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