├── .gitignore
├── Cargo.toml
├── LICENSE
├── README.md
├── behavioral
├── chain_of_responsibility.rs
├── command.rs
├── iterator.rs
├── observer.rs
├── state.rs
└── strategy.rs
├── creational
├── abstract_factory.rs
├── builder.rs
├── factory.rs
└── singleton.rs
├── rust-logo.png
└── structural
├── adapter.rs
├── decorator.rs
└── proxy.rs
/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | /target/
4 |
5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
7 | Cargo.lock
8 |
9 | # These are backup files generated by rustfmt
10 | **/*.rs.bk
11 | .vscode/
12 | .DS_Store
13 | .history
14 | .idea
15 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "rust_design_pattern"
3 | version = "0.1.0"
4 | authors = ["lpxxn "]
5 | edition = "2018"
6 |
7 | [[bin]]
8 | name = "factory"
9 | path = "./creational/factory.rs"
10 |
11 | [[bin]]
12 | name = "abstract_factory"
13 | path = "./creational/abstract_factory.rs"
14 |
15 | [[bin]]
16 | name = "builder"
17 | path = "./creational/builder.rs"
18 |
19 | [[bin]]
20 | name = "strategy"
21 | path = "./behavioral/strategy.rs"
22 |
23 | [[bin]]
24 | name = "state"
25 | path = "./behavioral/state.rs"
26 |
27 | [[bin]]
28 | name = "command"
29 | path = "./behavioral/command.rs"
30 |
31 | [[bin]]
32 | name = "iterator"
33 | path = "./behavioral/iterator.rs"
34 |
35 | [[bin]]
36 | name = "observer"
37 | path = "./behavioral/observer.rs"
38 |
39 | [[bin]]
40 | name = "chain_of_responsibility"
41 | path = "./behavioral/chain_of_responsibility.rs"
42 |
43 | [[bin]]
44 | name = "adapter"
45 | path = "./structural/adapter.rs"
46 |
47 | [[bin]]
48 | name = "decorator"
49 | path = "./structural/decorator.rs"
50 |
51 | [[bin]]
52 | name = "proxy"
53 | path = "./structural/proxy.rs"
54 |
55 | [[bin]]
56 | name = "singleton"
57 | path = "./creational/singleton.rs"
58 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Mozilla Public License Version 2.0
2 | ==================================
3 |
4 | 1. Definitions
5 | --------------
6 |
7 | 1.1. "Contributor"
8 | means each individual or legal entity that creates, contributes to
9 | the creation of, or owns Covered Software.
10 |
11 | 1.2. "Contributor Version"
12 | means the combination of the Contributions of others (if any) used
13 | by a Contributor and that particular Contributor's Contribution.
14 |
15 | 1.3. "Contribution"
16 | means Covered Software of a particular Contributor.
17 |
18 | 1.4. "Covered Software"
19 | means Source Code Form to which the initial Contributor has attached
20 | the notice in Exhibit A, the Executable Form of such Source Code
21 | Form, and Modifications of such Source Code Form, in each case
22 | including portions thereof.
23 |
24 | 1.5. "Incompatible With Secondary Licenses"
25 | means
26 |
27 | (a) that the initial Contributor has attached the notice described
28 | in Exhibit B to the Covered Software; or
29 |
30 | (b) that the Covered Software was made available under the terms of
31 | version 1.1 or earlier of the License, but not also under the
32 | terms of a Secondary License.
33 |
34 | 1.6. "Executable Form"
35 | means any form of the work other than Source Code Form.
36 |
37 | 1.7. "Larger Work"
38 | means a work that combines Covered Software with other material, in
39 | a separate file or files, that is not Covered Software.
40 |
41 | 1.8. "License"
42 | means this document.
43 |
44 | 1.9. "Licensable"
45 | means having the right to grant, to the maximum extent possible,
46 | whether at the time of the initial grant or subsequently, any and
47 | all of the rights conveyed by this License.
48 |
49 | 1.10. "Modifications"
50 | means any of the following:
51 |
52 | (a) any file in Source Code Form that results from an addition to,
53 | deletion from, or modification of the contents of Covered
54 | Software; or
55 |
56 | (b) any new file in Source Code Form that contains any Covered
57 | Software.
58 |
59 | 1.11. "Patent Claims" of a Contributor
60 | means any patent claim(s), including without limitation, method,
61 | process, and apparatus claims, in any patent Licensable by such
62 | Contributor that would be infringed, but for the grant of the
63 | License, by the making, using, selling, offering for sale, having
64 | made, import, or transfer of either its Contributions or its
65 | Contributor Version.
66 |
67 | 1.12. "Secondary License"
68 | means either the GNU General Public License, Version 2.0, the GNU
69 | Lesser General Public License, Version 2.1, the GNU Affero General
70 | Public License, Version 3.0, or any later versions of those
71 | licenses.
72 |
73 | 1.13. "Source Code Form"
74 | means the form of the work preferred for making modifications.
75 |
76 | 1.14. "You" (or "Your")
77 | means an individual or a legal entity exercising rights under this
78 | License. For legal entities, "You" includes any entity that
79 | controls, is controlled by, or is under common control with You. For
80 | purposes of this definition, "control" means (a) the power, direct
81 | or indirect, to cause the direction or management of such entity,
82 | whether by contract or otherwise, or (b) ownership of more than
83 | fifty percent (50%) of the outstanding shares or beneficial
84 | ownership of such entity.
85 |
86 | 2. License Grants and Conditions
87 | --------------------------------
88 |
89 | 2.1. Grants
90 |
91 | Each Contributor hereby grants You a world-wide, royalty-free,
92 | non-exclusive license:
93 |
94 | (a) under intellectual property rights (other than patent or trademark)
95 | Licensable by such Contributor to use, reproduce, make available,
96 | modify, display, perform, distribute, and otherwise exploit its
97 | Contributions, either on an unmodified basis, with Modifications, or
98 | as part of a Larger Work; and
99 |
100 | (b) under Patent Claims of such Contributor to make, use, sell, offer
101 | for sale, have made, import, and otherwise transfer either its
102 | Contributions or its Contributor Version.
103 |
104 | 2.2. Effective Date
105 |
106 | The licenses granted in Section 2.1 with respect to any Contribution
107 | become effective for each Contribution on the date the Contributor first
108 | distributes such Contribution.
109 |
110 | 2.3. Limitations on Grant Scope
111 |
112 | The licenses granted in this Section 2 are the only rights granted under
113 | this License. No additional rights or licenses will be implied from the
114 | distribution or licensing of Covered Software under this License.
115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a
116 | Contributor:
117 |
118 | (a) for any code that a Contributor has removed from Covered Software;
119 | or
120 |
121 | (b) for infringements caused by: (i) Your and any other third party's
122 | modifications of Covered Software, or (ii) the combination of its
123 | Contributions with other software (except as part of its Contributor
124 | Version); or
125 |
126 | (c) under Patent Claims infringed by Covered Software in the absence of
127 | its Contributions.
128 |
129 | This License does not grant any rights in the trademarks, service marks,
130 | or logos of any Contributor (except as may be necessary to comply with
131 | the notice requirements in Section 3.4).
132 |
133 | 2.4. Subsequent Licenses
134 |
135 | No Contributor makes additional grants as a result of Your choice to
136 | distribute the Covered Software under a subsequent version of this
137 | License (see Section 10.2) or under the terms of a Secondary License (if
138 | permitted under the terms of Section 3.3).
139 |
140 | 2.5. Representation
141 |
142 | Each Contributor represents that the Contributor believes its
143 | Contributions are its original creation(s) or it has sufficient rights
144 | to grant the rights to its Contributions conveyed by this License.
145 |
146 | 2.6. Fair Use
147 |
148 | This License is not intended to limit any rights You have under
149 | applicable copyright doctrines of fair use, fair dealing, or other
150 | equivalents.
151 |
152 | 2.7. Conditions
153 |
154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155 | in Section 2.1.
156 |
157 | 3. Responsibilities
158 | -------------------
159 |
160 | 3.1. Distribution of Source Form
161 |
162 | All distribution of Covered Software in Source Code Form, including any
163 | Modifications that You create or to which You contribute, must be under
164 | the terms of this License. You must inform recipients that the Source
165 | Code Form of the Covered Software is governed by the terms of this
166 | License, and how they can obtain a copy of this License. You may not
167 | attempt to alter or restrict the recipients' rights in the Source Code
168 | Form.
169 |
170 | 3.2. Distribution of Executable Form
171 |
172 | If You distribute Covered Software in Executable Form then:
173 |
174 | (a) such Covered Software must also be made available in Source Code
175 | Form, as described in Section 3.1, and You must inform recipients of
176 | the Executable Form how they can obtain a copy of such Source Code
177 | Form by reasonable means in a timely manner, at a charge no more
178 | than the cost of distribution to the recipient; and
179 |
180 | (b) You may distribute such Executable Form under the terms of this
181 | License, or sublicense it under different terms, provided that the
182 | license for the Executable Form does not attempt to limit or alter
183 | the recipients' rights in the Source Code Form under this License.
184 |
185 | 3.3. Distribution of a Larger Work
186 |
187 | You may create and distribute a Larger Work under terms of Your choice,
188 | provided that You also comply with the requirements of this License for
189 | the Covered Software. If the Larger Work is a combination of Covered
190 | Software with a work governed by one or more Secondary Licenses, and the
191 | Covered Software is not Incompatible With Secondary Licenses, this
192 | License permits You to additionally distribute such Covered Software
193 | under the terms of such Secondary License(s), so that the recipient of
194 | the Larger Work may, at their option, further distribute the Covered
195 | Software under the terms of either this License or such Secondary
196 | License(s).
197 |
198 | 3.4. Notices
199 |
200 | You may not remove or alter the substance of any license notices
201 | (including copyright notices, patent notices, disclaimers of warranty,
202 | or limitations of liability) contained within the Source Code Form of
203 | the Covered Software, except that You may alter any license notices to
204 | the extent required to remedy known factual inaccuracies.
205 |
206 | 3.5. Application of Additional Terms
207 |
208 | You may choose to offer, and to charge a fee for, warranty, support,
209 | indemnity or liability obligations to one or more recipients of Covered
210 | Software. However, You may do so only on Your own behalf, and not on
211 | behalf of any Contributor. You must make it absolutely clear that any
212 | such warranty, support, indemnity, or liability obligation is offered by
213 | You alone, and You hereby agree to indemnify every Contributor for any
214 | liability incurred by such Contributor as a result of warranty, support,
215 | indemnity or liability terms You offer. You may include additional
216 | disclaimers of warranty and limitations of liability specific to any
217 | jurisdiction.
218 |
219 | 4. Inability to Comply Due to Statute or Regulation
220 | ---------------------------------------------------
221 |
222 | If it is impossible for You to comply with any of the terms of this
223 | License with respect to some or all of the Covered Software due to
224 | statute, judicial order, or regulation then You must: (a) comply with
225 | the terms of this License to the maximum extent possible; and (b)
226 | describe the limitations and the code they affect. Such description must
227 | be placed in a text file included with all distributions of the Covered
228 | Software under this License. Except to the extent prohibited by statute
229 | or regulation, such description must be sufficiently detailed for a
230 | recipient of ordinary skill to be able to understand it.
231 |
232 | 5. Termination
233 | --------------
234 |
235 | 5.1. The rights granted under this License will terminate automatically
236 | if You fail to comply with any of its terms. However, if You become
237 | compliant, then the rights granted under this License from a particular
238 | Contributor are reinstated (a) provisionally, unless and until such
239 | Contributor explicitly and finally terminates Your grants, and (b) on an
240 | ongoing basis, if such Contributor fails to notify You of the
241 | non-compliance by some reasonable means prior to 60 days after You have
242 | come back into compliance. Moreover, Your grants from a particular
243 | Contributor are reinstated on an ongoing basis if such Contributor
244 | notifies You of the non-compliance by some reasonable means, this is the
245 | first time You have received notice of non-compliance with this License
246 | from such Contributor, and You become compliant prior to 30 days after
247 | Your receipt of the notice.
248 |
249 | 5.2. If You initiate litigation against any entity by asserting a patent
250 | infringement claim (excluding declaratory judgment actions,
251 | counter-claims, and cross-claims) alleging that a Contributor Version
252 | directly or indirectly infringes any patent, then the rights granted to
253 | You by any and all Contributors for the Covered Software under Section
254 | 2.1 of this License shall terminate.
255 |
256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257 | end user license agreements (excluding distributors and resellers) which
258 | have been validly granted by You or Your distributors under this License
259 | prior to termination shall survive termination.
260 |
261 | ************************************************************************
262 | * *
263 | * 6. Disclaimer of Warranty *
264 | * ------------------------- *
265 | * *
266 | * Covered Software is provided under this License on an "as is" *
267 | * basis, without warranty of any kind, either expressed, implied, or *
268 | * statutory, including, without limitation, warranties that the *
269 | * Covered Software is free of defects, merchantable, fit for a *
270 | * particular purpose or non-infringing. The entire risk as to the *
271 | * quality and performance of the Covered Software is with You. *
272 | * Should any Covered Software prove defective in any respect, You *
273 | * (not any Contributor) assume the cost of any necessary servicing, *
274 | * repair, or correction. This disclaimer of warranty constitutes an *
275 | * essential part of this License. No use of any Covered Software is *
276 | * authorized under this License except under this disclaimer. *
277 | * *
278 | ************************************************************************
279 |
280 | ************************************************************************
281 | * *
282 | * 7. Limitation of Liability *
283 | * -------------------------- *
284 | * *
285 | * Under no circumstances and under no legal theory, whether tort *
286 | * (including negligence), contract, or otherwise, shall any *
287 | * Contributor, or anyone who distributes Covered Software as *
288 | * permitted above, be liable to You for any direct, indirect, *
289 | * special, incidental, or consequential damages of any character *
290 | * including, without limitation, damages for lost profits, loss of *
291 | * goodwill, work stoppage, computer failure or malfunction, or any *
292 | * and all other commercial damages or losses, even if such party *
293 | * shall have been informed of the possibility of such damages. This *
294 | * limitation of liability shall not apply to liability for death or *
295 | * personal injury resulting from such party's negligence to the *
296 | * extent applicable law prohibits such limitation. Some *
297 | * jurisdictions do not allow the exclusion or limitation of *
298 | * incidental or consequential damages, so this exclusion and *
299 | * limitation may not apply to You. *
300 | * *
301 | ************************************************************************
302 |
303 | 8. Litigation
304 | -------------
305 |
306 | Any litigation relating to this License may be brought only in the
307 | courts of a jurisdiction where the defendant maintains its principal
308 | place of business and such litigation shall be governed by laws of that
309 | jurisdiction, without reference to its conflict-of-law provisions.
310 | Nothing in this Section shall prevent a party's ability to bring
311 | cross-claims or counter-claims.
312 |
313 | 9. Miscellaneous
314 | ----------------
315 |
316 | This License represents the complete agreement concerning the subject
317 | matter hereof. If any provision of this License is held to be
318 | unenforceable, such provision shall be reformed only to the extent
319 | necessary to make it enforceable. Any law or regulation which provides
320 | that the language of a contract shall be construed against the drafter
321 | shall not be used to construe this License against a Contributor.
322 |
323 | 10. Versions of the License
324 | ---------------------------
325 |
326 | 10.1. New Versions
327 |
328 | Mozilla Foundation is the license steward. Except as provided in Section
329 | 10.3, no one other than the license steward has the right to modify or
330 | publish new versions of this License. Each version will be given a
331 | distinguishing version number.
332 |
333 | 10.2. Effect of New Versions
334 |
335 | You may distribute the Covered Software under the terms of the version
336 | of the License under which You originally received the Covered Software,
337 | or under the terms of any subsequent version published by the license
338 | steward.
339 |
340 | 10.3. Modified Versions
341 |
342 | If you create software not governed by this License, and you want to
343 | create a new license for such software, you may create and use a
344 | modified version of this License if you rename the license and remove
345 | any references to the name of the license steward (except to note that
346 | such modified license differs from this License).
347 |
348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary
349 | Licenses
350 |
351 | If You choose to distribute Source Code Form that is Incompatible With
352 | Secondary Licenses under the terms of this version of the License, the
353 | notice described in Exhibit B of this License must be attached.
354 |
355 | Exhibit A - Source Code Form License Notice
356 | -------------------------------------------
357 |
358 | This Source Code Form is subject to the terms of the Mozilla Public
359 | License, v. 2.0. If a copy of the MPL was not distributed with this
360 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
361 |
362 | If it is not possible or desirable to put the notice in a particular
363 | file, then You may include the notice in a location (such as a LICENSE
364 | file in a relevant directory) where a recipient would be likely to look
365 | for such a notice.
366 |
367 | You may add additional accurate notices of copyright ownership.
368 |
369 | Exhibit B - "Incompatible With Secondary Licenses" Notice
370 | ---------------------------------------------------------
371 |
372 | This Source Code Form is "Incompatible With Secondary Licenses", as
373 | defined by the Mozilla Public License, v. 2.0.
374 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Rust Design Patterns
5 |
6 |
7 |
8 | ## Creational Patterns
9 |
10 | | Pattern | Description | Status |
11 | |:-------:|:----------- |:------:|
12 | | [Factory Method](/creational/factory.rs) | Defers instantiation of an object to a specialized function for creating instances | ✔ |
13 | | [Abstract Factory](/creational/abstract_factory.rs) | Provides an interface for creating families of releated objects | ✔ |
14 | | [Builder](/creational/builder.rs) | Builds a complex object using simple objects | ✔ |
15 | | [Singleton](/creational/singleton.rs) | Restricts instantiation of a type to one object | ✔ |
16 |
17 |
18 | ## Behavioral Patterns
19 | | Pattern | Description | Status |
20 | |:-------:|:----------- |:------:|
21 | | [Strategy](/behavioral/strategy.rs) | Enables an algorithm's behavior to be selected at runtime | ✔ |
22 | | [State](/behavioral/state.rs) | Encapsulates varying behavior for the same object based on its internal state | ✔ |
23 | | [Command](/behavioral/command.rs) | Converts requests or simple operations into objects. | ✔ |
24 | | [Iterator](/behavioral/iterator.rs) | Lets you traverse elements of a collection without exposing its underlying representation | ✔ |
25 | | [Observer](/behavioral/observer.rs) | Allows one objects to notify other objects about changes in their state. | ✔ |
26 | | [Chain of Responsibility](/behavioral/chain_of_responsibility.rs) | Avoids coupling a sender to receiver by giving more than object a chance to handle the request | ✔ |
27 |
28 |
29 |
30 |
31 | ## Structural Patterns
32 |
33 | | Pattern | Description | Status |
34 | |:-------:|:----------- |:------:|
35 | | [Adapter](/structural/adapter.rs) | allows objects with incompatible interfaces to collaborate. | ✔ |
36 | | [Decorator](/structural/decorator.rs) | Adds behavior to an object, statically or dynamically | ✔ |
37 | | [Proxy](/structural/proxy.rs) | Provides a surrogate for an object to control it's actions | ✔ |
38 |
39 |
--------------------------------------------------------------------------------
/behavioral/chain_of_responsibility.rs:
--------------------------------------------------------------------------------
1 | //! Chain of Responsibility is a behavioral design pattern that lets you pass requests along a chain of handlers.
2 | //! Upon receiving a request, each handler decides either to process the request or to pass it to the next handler in the chain.
3 |
4 | // The Handler trait declares a method for building the chain of
5 | // handlers. It also declares a method for executing a request.
6 | trait Handler<'a> {
7 | fn set_next(&mut self, next: &'a dyn Handler<'a>) -> &mut dyn Handler<'a>;
8 | fn handle(&self, request: &str);
9 | }
10 |
11 | struct AHandler<'a> {
12 | name: String,
13 | next: Option<&'a dyn Handler<'a>>,
14 | }
15 | impl<'a> AHandler<'a> {
16 | fn new(name: String) -> AHandler<'a> {
17 | AHandler { name, next: None }
18 | }
19 | }
20 | impl<'a> Handler<'a> for AHandler<'a> {
21 | fn set_next(&mut self, next: &'a dyn Handler<'a>) -> &mut dyn Handler<'a> {
22 | self.next = Some(next);
23 | self
24 | }
25 | fn handle(&self, request: &str) {
26 | println!("{} handle the request: {}", self.name, request);
27 | if let Some(v) = &self.next {
28 | v.handle(request);
29 | }
30 | }
31 | }
32 |
33 | struct BHandler<'a> {
34 | next: Option<&'a dyn Handler<'a>>,
35 | }
36 | impl<'a> BHandler<'a> {
37 | fn new() -> BHandler<'a> {
38 | BHandler { next: None }
39 | }
40 | }
41 | impl<'a> Handler<'a> for BHandler<'a> {
42 | fn set_next(&mut self, next: &'a dyn Handler<'a>) -> &mut dyn Handler<'a> {
43 | self.next = Some(next);
44 | self
45 | }
46 | fn handle(&self, request: &str) {
47 | println!("BHandler handle the request: {}", request);
48 | if let Some(v) = &self.next {
49 | v.handle(request);
50 | }
51 | }
52 | }
53 |
54 | struct Client;
55 | impl<'a> Client {
56 | fn handle>(h: &T) {
57 | h.handle("do something...")
58 | }
59 | }
60 |
61 | fn main() {
62 | let a1 = AHandler::new("dog".to_string());
63 | Client::handle(&a1);
64 |
65 | println!();
66 | let mut b = BHandler::new();
67 | let mut a2 = AHandler::new("cat".to_string());
68 | b.set_next(&a1);
69 | // or
70 | // let h = b.set_next(&a1);
71 | //a2.set_next(h);
72 | a2.set_next(&b);
73 | Client::handle(&a2);
74 | }
75 |
--------------------------------------------------------------------------------
/behavioral/command.rs:
--------------------------------------------------------------------------------
1 | //! Each action is encapsulated into a struct with the trait Command
2 |
3 | use std::collections::HashMap;
4 |
5 | trait Command {
6 | fn execute(&self);
7 | }
8 |
9 | #[derive(Copy, Clone)]
10 | struct TV;
11 | impl TV {
12 | fn new() -> TV {
13 | TV
14 | }
15 | fn on(&self) {
16 | println!("TV is on, watch movies.");
17 | }
18 | fn off(&self) {
19 | println!("TV is off");
20 | }
21 | }
22 |
23 | struct TVOnCommand {
24 | tv: TV,
25 | }
26 |
27 | impl TVOnCommand {
28 | fn new(tv: TV) -> TVOnCommand {
29 | TVOnCommand { tv }
30 | }
31 | }
32 |
33 | impl Command for TVOnCommand {
34 | fn execute(&self) {
35 | self.tv.on();
36 | }
37 | }
38 |
39 | struct TVOffCommand {
40 | tv: TV,
41 | }
42 |
43 | impl TVOffCommand {
44 | fn new(tv: TV) -> TVOffCommand {
45 | TVOffCommand { tv }
46 | }
47 | }
48 |
49 | impl Command for TVOffCommand {
50 | fn execute(&self) {
51 | self.tv.off();
52 | }
53 | }
54 |
55 | struct TVRemoteControl {
56 | commands: HashMap>,
57 | }
58 |
59 | impl TVRemoteControl {
60 | fn new() -> TVRemoteControl {
61 | TVRemoteControl {
62 | commands: HashMap::new(),
63 | }
64 | }
65 | fn set_command(&mut self, idx: i32, cmd: Box) {
66 | self.commands.insert(idx, cmd);
67 | }
68 | fn press_button(&self, idx: i32) {
69 | if let Some(cmd) = self.commands.get(&idx) {
70 | cmd.execute();
71 | } else {
72 | println!("do nothing.");
73 | }
74 | }
75 | }
76 |
77 | fn main() {
78 | let tv = TV::new();
79 | let mut remote_control = TVRemoteControl::new();
80 | remote_control.press_button(0);
81 |
82 | remote_control.set_command(1, Box::new(TVOnCommand::new(tv)));
83 | remote_control.set_command(2, Box::new(TVOffCommand::new(tv)));
84 |
85 | remote_control.press_button(1);
86 | remote_control.press_button(2);
87 | }
88 |
--------------------------------------------------------------------------------
/behavioral/iterator.rs:
--------------------------------------------------------------------------------
1 | //! Iterator is a behavioral design pattern that lets you traverse elements of a collection without exposing its underlying representation (list, stack, tree, etc.).
2 |
3 | trait Iterator {
4 | fn next(&mut self) -> Option;
5 | fn current(&self) -> Option;
6 | fn has_next(&self) -> bool;
7 | fn reset(&mut self);
8 | }
9 |
10 | struct Container {
11 | data: Vec,
12 | }
13 |
14 | struct ConcreteIterator<'a, T> {
15 | idx: usize,
16 | container: &'a Container,
17 | }
18 |
19 | impl<'a, T: Clone> ConcreteIterator<'a, T> {
20 | fn new(container: &'a Container) -> ConcreteIterator {
21 | ConcreteIterator { idx: 0, container }
22 | }
23 | }
24 |
25 | impl<'a, T: Clone> Iterator for ConcreteIterator<'a, T> {
26 | fn next(&mut self) -> Option {
27 | let item = self.container.data.get(self.idx).cloned();
28 | self.idx += 1;
29 | item
30 | }
31 | fn current(&self) -> Option {
32 | self.container.data.get(self.idx).cloned()
33 | }
34 | fn has_next(&self) -> bool {
35 | self.container.data.len() > self.idx
36 | }
37 | fn reset(&mut self) {
38 | self.idx = 0;
39 | }
40 | }
41 |
42 | impl Container {
43 | fn new() -> Container {
44 | Container { data: Vec::new() }
45 | }
46 | fn add_item(&mut self, item: T) {
47 | self.data.push(item);
48 | }
49 | fn iter(&self) -> impl Iterator + '_ {
50 | ConcreteIterator::new(self)
51 | }
52 | }
53 |
54 | fn main() {
55 | let mut c = Container::new();
56 | c.add_item(1);
57 | c.add_item(2);
58 | c.add_item(3);
59 |
60 | let mut iter = c.iter();
61 | let has_next = iter.has_next();
62 | assert_eq!(has_next, true);
63 | let item = iter.next();
64 | println!("item: {:?}", item);
65 | iter.reset();
66 | while iter.has_next() {
67 | let v = iter.next().unwrap();
68 | println!("item: {}", v);
69 | }
70 | let item = iter.next();
71 | assert_eq!(item, None);
72 | }
73 |
--------------------------------------------------------------------------------
/behavioral/observer.rs:
--------------------------------------------------------------------------------
1 | //! Observer is a behavioral design pattern that allows one objects to notify other objects about changes in their state.
2 |
3 | trait IObserver {
4 | fn update(&self);
5 | }
6 |
7 | trait ISubject<'a, T: IObserver> {
8 | fn attach(&mut self, observer: &'a T);
9 | fn detach(&mut self, observer: &'a T);
10 | fn notify_observers(&self);
11 | }
12 |
13 | struct Subject<'a, T: IObserver> {
14 | observers: Vec<&'a T>,
15 | }
16 | impl<'a, T: IObserver + PartialEq> Subject<'a, T> {
17 | fn new() -> Subject<'a, T> {
18 | Subject {
19 | observers: Vec::new(),
20 | }
21 | }
22 | }
23 |
24 | impl<'a, T: IObserver + PartialEq> ISubject<'a, T> for Subject<'a, T> {
25 | fn attach(&mut self, observer: &'a T) {
26 | self.observers.push(observer);
27 | }
28 | fn detach(&mut self, observer: &'a T) {
29 | if let Some(idx) = self.observers.iter().position(|x| *x == observer) {
30 | self.observers.remove(idx);
31 | }
32 | }
33 | fn notify_observers(&self) {
34 | for item in self.observers.iter() {
35 | item.update();
36 | }
37 | }
38 | }
39 |
40 | #[derive(PartialEq)]
41 | struct ConcreteObserver {
42 | id: i32,
43 | }
44 | impl IObserver for ConcreteObserver {
45 | fn update(&self) {
46 | println!("Observer id:{} received event!", self.id);
47 | }
48 | }
49 |
50 | fn main() {
51 | let mut subject = Subject::new();
52 | let observer_a = ConcreteObserver { id: 1 };
53 | let observer_b = ConcreteObserver { id: 2 };
54 |
55 | subject.attach(&observer_a);
56 | subject.attach(&observer_b);
57 | subject.notify_observers();
58 |
59 | subject.detach(&observer_b);
60 | subject.notify_observers();
61 | }
62 |
--------------------------------------------------------------------------------
/behavioral/state.rs:
--------------------------------------------------------------------------------
1 | //! State is a behavioral design pattern that lets an object alter its behavior when its internal state changes.
2 | //! It appears as if the object changed its class.
3 |
4 | //! We’ll implement a blog post workflow
5 | //! 1. A blog post starts as an empty draft.
6 | //! 2. When the draft is done, a review of the post is requested.
7 | //! 3. When the post is approved, it gets published.
8 | //! 4. Only published blog posts return content to print, so unapproved posts can’t accidentally be published.
9 |
10 | trait State {
11 | fn request_review(self: Box) -> Box;
12 | fn approve(self: Box) -> Box;
13 | fn content<'a>(&self, _post: &'a Post) -> &'a str {
14 | ""
15 | }
16 | }
17 |
18 | struct Draft;
19 | impl State for Draft {
20 | fn request_review(self: Box) -> Box {
21 | Box::new(PendingReview {})
22 | }
23 | fn approve(self: Box) -> Box {
24 | self
25 | }
26 | }
27 |
28 | struct PendingReview;
29 | impl State for PendingReview {
30 | fn request_review(self: Box) -> Box {
31 | self
32 | }
33 | fn approve(self: Box) -> Box {
34 | Box::new(Published {})
35 | }
36 | }
37 |
38 | struct Published;
39 | impl State for Published {
40 | fn request_review(self: Box) -> Box {
41 | self
42 | }
43 | fn approve(self: Box) -> Box {
44 | self
45 | }
46 | fn content<'a>(&self, post: &'a Post) -> &'a str {
47 | &post.content
48 | }
49 | }
50 |
51 | struct Post {
52 | state: Option>,
53 | content: String,
54 | }
55 |
56 | impl Post {
57 | fn new() -> Post {
58 | Post {
59 | state: Some(Box::new(Draft {})),
60 | content: String::new(),
61 | }
62 | }
63 | fn add_text(&mut self, text: &str) {
64 | self.content.push_str(text);
65 | }
66 | fn content(&self) -> &str {
67 | self.state.as_ref().unwrap().content(self)
68 | }
69 | fn request_review(&mut self) {
70 | if let Some(s) = self.state.take() {
71 | self.state = Some(s.request_review())
72 | }
73 | }
74 | fn approve(&mut self) {
75 | if let Some(s) = self.state.take() {
76 | self.state = Some(s.approve())
77 | }
78 | }
79 | }
80 |
81 | fn main() {
82 | let mut post = Post::new();
83 |
84 | let text = "State is a behavioral design pattern.";
85 | post.add_text(text);
86 | assert_eq!("", post.content());
87 |
88 | post.request_review();
89 | assert_eq!("", post.content());
90 |
91 | post.approve();
92 | assert_eq!(text, post.content());
93 | println!("post content: {}", post.content());
94 | }
95 |
--------------------------------------------------------------------------------
/behavioral/strategy.rs:
--------------------------------------------------------------------------------
1 | trait FlyBehaviour {
2 | fn fly(&self);
3 | }
4 |
5 | struct FlyWithWings;
6 |
7 | impl FlyBehaviour for FlyWithWings {
8 | fn fly(&self) {
9 | println!("i can fly!")
10 | }
11 | }
12 |
13 | struct FlyNoWay;
14 |
15 | impl FlyBehaviour for FlyNoWay {
16 | fn fly(&self) {
17 | println!("i can't fly!~~")
18 | }
19 | }
20 |
21 | trait Duck {
22 | fn get_fly_behaviour(&self) -> &dyn FlyBehaviour;
23 | fn fly(&self) {
24 | let s = self.get_fly_behaviour();
25 | s.fly();
26 | }
27 | }
28 |
29 | struct MallardDuck {
30 | fly_behaviour: Box,
31 | }
32 |
33 | impl Duck for MallardDuck {
34 | fn get_fly_behaviour(&self) -> &dyn FlyBehaviour {
35 | return &(*self.fly_behaviour);
36 | }
37 | }
38 |
39 | impl MallardDuck {
40 | fn new(fly_behaviour: Box) -> Self {
41 | MallardDuck { fly_behaviour }
42 | }
43 | fn set_fly_behaviour(&mut self, fly_behaviour: Box) {
44 | self.fly_behaviour = fly_behaviour;
45 | }
46 | }
47 |
48 | struct ModelDuck {
49 | fly_behaviour: Box,
50 | }
51 |
52 | impl Duck for ModelDuck {
53 | fn get_fly_behaviour(&self) -> &dyn FlyBehaviour {
54 | return &(*self.fly_behaviour);
55 | }
56 | }
57 |
58 | impl ModelDuck {
59 | fn new(fly_behaviour: Box) -> Self {
60 | ModelDuck { fly_behaviour }
61 | }
62 | }
63 |
64 | pub fn main() {
65 | let mut mallard_duck = MallardDuck::new(Box::new(FlyWithWings));
66 | mallard_duck.fly();
67 | mallard_duck.set_fly_behaviour(Box::new(FlyNoWay));
68 | mallard_duck.fly();
69 |
70 | let model_duck = ModelDuck::new(Box::new(FlyNoWay));
71 | model_duck.fly();
72 | }
73 |
--------------------------------------------------------------------------------
/creational/abstract_factory.rs:
--------------------------------------------------------------------------------
1 | //! Abstract Factory is a creational design pattern that lets you produce families of related objects without specifying their concrete classes.
2 |
3 | trait GUIFactory {
4 | fn create_button(&self) -> Box;
5 | fn create_checkbox(&self) -> Box;
6 | }
7 |
8 | struct WinFactory;
9 | impl GUIFactory for WinFactory {
10 | fn create_button(&self) -> Box {
11 | Box::new(WinButton {})
12 | }
13 | fn create_checkbox(&self) -> Box {
14 | Box::new(WinCheckbox {})
15 | }
16 | }
17 |
18 | struct MacFactory;
19 | impl GUIFactory for MacFactory {
20 | fn create_button(&self) -> Box {
21 | Box::new(MacButton {})
22 | }
23 | fn create_checkbox(&self) -> Box {
24 | Box::new(MacCheckbox {})
25 | }
26 | }
27 |
28 | trait Button {
29 | fn paint(&self);
30 | }
31 |
32 | struct WinButton;
33 | impl Button for WinButton {
34 | fn paint(&self) {
35 | println!("windows os button");
36 | }
37 | }
38 |
39 | struct MacButton;
40 | impl Button for MacButton {
41 | fn paint(&self) {
42 | println!("mac os button");
43 | }
44 | }
45 |
46 | trait Checkbox {
47 | fn paint(&self);
48 | }
49 |
50 | struct WinCheckbox;
51 | impl Checkbox for WinCheckbox {
52 | fn paint(&self) {
53 | println!("windows os checkbox");
54 | }
55 | }
56 |
57 | struct MacCheckbox;
58 | impl Checkbox for MacCheckbox {
59 | fn paint(&self) {
60 | println!("mac os checkbox");
61 | }
62 | }
63 |
64 | struct Application;
65 | impl Application {
66 | fn new_gui_factory(os: &str) -> Box {
67 | match os {
68 | "mac" => Box::new(MacFactory {}),
69 | "win" => Box::new(WinFactory {}),
70 | _ => panic!("error"),
71 | }
72 | }
73 | }
74 |
75 | fn main() {
76 | let mac_app = Application::new_gui_factory("mac");
77 | let btn = mac_app.create_button();
78 | btn.paint(); // output: mac os button
79 | let cb = mac_app.create_checkbox();
80 | cb.paint(); // output: mac os checkbox
81 |
82 | let win_app = Application::new_gui_factory("win");
83 | let btn = win_app.create_button();
84 | btn.paint(); // output: windows os button
85 | let cb = win_app.create_checkbox();
86 | cb.paint(); // output: windows os checkbox
87 | }
88 |
--------------------------------------------------------------------------------
/creational/builder.rs:
--------------------------------------------------------------------------------
1 | //! Builder is a creational design pattern, which allows constructing complex objects step by step.
2 |
3 | #[derive(Clone)]
4 | struct Product {
5 | parts: Vec,
6 | }
7 |
8 | impl Product {
9 | fn new() -> Product {
10 | Product { parts: Vec::new() }
11 | }
12 | fn list_parts(&self) {
13 | let parts_list = String::from(" parts ");
14 | println!("{0}{1}{0}", "*".repeat(10), parts_list);
15 | for v in &self.parts {
16 | println!("{}", v);
17 | }
18 | println!("{0}{1}{0}", "*".repeat(10), "*".repeat(parts_list.len()));
19 | }
20 | }
21 |
22 | /**
23 | * The Builder trait specifies methods for creating the different parts of
24 | * the Product objects.
25 | */
26 | trait Builder {
27 | fn produce_part_a(&mut self);
28 | fn produce_part_b(&mut self);
29 | fn produce_part_c(&mut self);
30 | fn get_product(&mut self) -> Product;
31 | }
32 |
33 | /**
34 | * The Concrete Builder classes follow the Builder interface and provide
35 | * specific implementations of the building steps. Your program may have several
36 | * variations of Builders, implemented differently.
37 | */
38 | struct ContreteBuilder1 {
39 | product: Product,
40 | }
41 |
42 | impl ContreteBuilder1 {
43 | fn new() -> ContreteBuilder1 {
44 | ContreteBuilder1 {
45 | product: Product::new(),
46 | }
47 | }
48 | }
49 |
50 | impl Builder for ContreteBuilder1 {
51 | fn produce_part_a(&mut self) {
52 | self.product.parts.push("part a1".to_string());
53 | }
54 | fn produce_part_b(&mut self) {
55 | self.product.parts.push("part b1".to_string());
56 | }
57 | fn produce_part_c(&mut self) {
58 | self.product.parts.push("part c1".to_string());
59 | }
60 | fn get_product(&mut self) -> Product {
61 | let p = self.product.clone();
62 | self.product = Product::new();
63 | p
64 | }
65 | }
66 |
67 | struct ContreteBuilder2 {
68 | product: Product,
69 | }
70 |
71 | impl ContreteBuilder2 {
72 | fn new() -> ContreteBuilder2 {
73 | ContreteBuilder2 {
74 | product: Product::new(),
75 | }
76 | }
77 | }
78 |
79 | impl Builder for ContreteBuilder2 {
80 | fn produce_part_a(&mut self) {
81 | self.product.parts.push("part a ~~~~ 2".to_string());
82 | }
83 | fn produce_part_b(&mut self) {
84 | self.product.parts.push("part b ~~~~ 2".to_string());
85 | }
86 | fn produce_part_c(&mut self) {
87 | self.product.parts.push("part c ~~~~ 2".to_string());
88 | }
89 | fn get_product(&mut self) -> Product {
90 | let p = Product {
91 | parts: self.product.parts.clone(),
92 | ..self.product
93 | };
94 | self.product = Product::new();
95 | p
96 | }
97 | }
98 |
99 | /**
100 | * The Director is only responsible for executing the building steps in a
101 | * particular sequence. It is helpful when producing products according to a
102 | * specific order or configuration. Strictly speaking, the Director class is
103 | * optional, since the client can control builders directly.
104 | */
105 | struct Director {
106 | builder: Box,
107 | }
108 |
109 | impl Director {
110 | fn new(builder: Box) -> Director {
111 | Director { builder: builder }
112 | }
113 |
114 | fn construct(&mut self) {
115 | self.builder.produce_part_a();
116 | self.builder.produce_part_b();
117 | self.builder.produce_part_c();
118 | }
119 | }
120 |
121 | fn main() {
122 | let builder1 = Box::new(ContreteBuilder1::new());
123 | let mut direct = Director::new(builder1);
124 | direct.construct();
125 | let product = direct.builder.get_product();
126 | product.list_parts();
127 | // output:
128 | /*
129 | ********** parts **********
130 | part a1
131 | part b1
132 | part c1
133 | ***************************
134 | */
135 |
136 | let build2 = Box::new(ContreteBuilder2::new());
137 | let mut direct = Director::new(build2);
138 | direct.construct();
139 | let product = direct.builder.get_product();
140 | product.list_parts();
141 | // output:
142 | /*
143 | ********** parts **********
144 | part a ~~~~ 2
145 | part b ~~~~ 2
146 | part c ~~~~ 2
147 | ***************************
148 | */
149 | }
150 |
--------------------------------------------------------------------------------
/creational/factory.rs:
--------------------------------------------------------------------------------
1 | //! Factory method creational design pattern allows creating objects without having to specify the exact type of the object that will be created.
2 |
3 | trait Shape {
4 | fn draw(&self);
5 | }
6 |
7 | enum ShapeType {
8 | Rectangle,
9 | Circle,
10 | }
11 |
12 | struct Rectangle {}
13 |
14 | impl Shape for Rectangle {
15 | fn draw(&self) {
16 | println!("draw a rectangle!");
17 | }
18 | }
19 |
20 | struct Circle {}
21 |
22 | impl Shape for Circle {
23 | fn draw(&self) {
24 | println!("draw a circle!");
25 | }
26 | }
27 |
28 | struct ShapeFactory;
29 | impl ShapeFactory {
30 | fn new_shape(s: &ShapeType) -> Box {
31 | match s {
32 | ShapeType::Circle => Box::new(Circle {}),
33 | ShapeType::Rectangle => Box::new(Rectangle {}),
34 | }
35 | }
36 | }
37 |
38 | fn main() {
39 | let shape = ShapeFactory::new_shape(&ShapeType::Circle);
40 | shape.draw(); // output: draw a circle!
41 |
42 | let shape = ShapeFactory::new_shape(&ShapeType::Rectangle);
43 | shape.draw(); // output: draw a rectangle!
44 | }
45 |
--------------------------------------------------------------------------------
/creational/singleton.rs:
--------------------------------------------------------------------------------
1 | use std::mem::MaybeUninit;
2 | use std::sync::{Mutex, Once};
3 |
4 | #[derive(Debug)]
5 | struct Config {
6 | db_connection_str: String,
7 | }
8 |
9 | fn get_config() -> &'static Mutex {
10 | static mut CONF: MaybeUninit> = MaybeUninit::uninit();
11 | static ONCE: Once = Once::new();
12 |
13 | ONCE.call_once(|| unsafe {
14 | CONF.as_mut_ptr().write(Mutex::new(Config {
15 | db_connection_str: "test config".to_string(),
16 | }));
17 | });
18 |
19 | unsafe { &*CONF.as_ptr() }
20 | }
21 |
22 | fn main() {
23 | let f1 = get_config();
24 | println!("{:?}", f1);
25 | // modify
26 | {
27 | let mut conf = f1.lock().unwrap();
28 | conf.db_connection_str = "hello".to_string();
29 | }
30 |
31 | let f2 = get_config();
32 | println!("{:?}", f2);
33 | let conf2 = f2.lock().unwrap();
34 |
35 | assert_eq!(conf2.db_connection_str, "hello".to_string())
36 | }
37 |
--------------------------------------------------------------------------------
/rust-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lpxxn/rust-design-pattern/d020c4641af0b272a7984fdacb45af0827dfac57/rust-logo.png
--------------------------------------------------------------------------------
/structural/adapter.rs:
--------------------------------------------------------------------------------
1 | use std::rc::Rc;
2 |
3 | // The Target defines the domain-specific interface used by the client code.
4 | trait Target {
5 | fn get_request(&self) -> String {
6 | String::from("Target: The default target's behavior.")
7 | }
8 | }
9 |
10 | struct DefaultTarget;
11 | impl Target for DefaultTarget {}
12 |
13 | // The Adaptee contains some useful behavior, but its interface is
14 | // incompatible with the existing client code. The Adaptee needs some
15 | // adaptation before the client code can use it.
16 | struct Adaptee {
17 | req_str: String,
18 | }
19 |
20 | impl Adaptee {
21 | fn new(s: String) -> Adaptee {
22 | Adaptee { req_str: s }
23 | }
24 | fn specific_request(&self) -> String {
25 | format!("specific request: {}", self.req_str)
26 | }
27 | }
28 |
29 | // The Adapter makes the Adaptee's interface compatible with the Target's
30 | // interface.
31 | struct Adapter {
32 | adaptee: Rc,
33 | }
34 |
35 | impl Adapter {
36 | fn new(a: Rc) -> Adapter {
37 | Adapter { adaptee: a }
38 | }
39 | }
40 |
41 | impl Target for Adapter {
42 | fn get_request(&self) -> String {
43 | format!("Adapter: {}", self.adaptee.specific_request())
44 | }
45 | }
46 |
47 | // The client code supports all classes that follow the Target trait.
48 | struct Client;
49 | impl Client {
50 | fn client_code(target: &T) {
51 | println!("{}", target.get_request());
52 | }
53 | }
54 |
55 | fn main() {
56 | println!("Client: I can work just fine with the Target objects:");
57 | Client::client_code(&DefaultTarget {});
58 | let adaptee = Rc::new(Adaptee::new("hello world".to_string()));
59 | println!("Client: The Adaptee class has a weird interface. See, I don't understand it:");
60 | println!("Adaptee: {}", adaptee.specific_request());
61 |
62 | println!("Client: But I can work with it via the Adapter:");
63 | let adapter = Adapter::new(adaptee);
64 | Client::client_code(&adapter);
65 | }
66 |
--------------------------------------------------------------------------------
/structural/decorator.rs:
--------------------------------------------------------------------------------
1 | use std::rc::Rc;
2 |
3 | // The base Component trait defines operations that can be altered by
4 | // decorators.
5 | trait Component {
6 | fn operation(&self) -> String;
7 | }
8 |
9 | // Concrete Components provide default implementations of the operations.
10 | // There might be several variations of these classes.
11 | struct ConcreteComponent {}
12 |
13 | impl Component for ConcreteComponent {
14 | fn operation(&self) -> String {
15 | "ConcreteComponent".to_string()
16 | }
17 | }
18 |
19 | // The base Decorator class follows the same interface as the other
20 | // components. The primary purpose of this class is to define the wrapping
21 | // interface for all concrete decorators. The default implementation of the
22 | // wrapping code might include a field for storing a wrapped component and
23 | // the means to initialize it.
24 | trait Decorator: Component {
25 | fn new(component: Rc) -> Self;
26 | }
27 |
28 | // Concrete Decorators call the wrapped object and alter its result in some
29 | // way.
30 | struct ConcreteDecoratorA {
31 | component: Rc,
32 | }
33 |
34 | impl Decorator for ConcreteDecoratorA {
35 | fn new(component: Rc) -> Self {
36 | ConcreteDecoratorA { component }
37 | }
38 | }
39 |
40 | impl Component for ConcreteDecoratorA {
41 | fn operation(&self) -> String {
42 | format!("ConcreteDecoratorA: {}", self.component.operation())
43 | }
44 | }
45 |
46 | struct ConcreteDecoratorB {
47 | component: Rc,
48 | }
49 |
50 | impl Decorator for ConcreteDecoratorB {
51 | fn new(component: Rc) -> Self {
52 | ConcreteDecoratorB { component }
53 | }
54 | }
55 |
56 | impl Component for ConcreteDecoratorB {
57 | fn operation(&self) -> String {
58 | format!("ConcreteDecoratorB: {}", self.component.operation())
59 | }
60 | }
61 |
62 | // The client code works with all objects using the Component interface.
63 | // This way it can stay independent of the concrete classes of
64 | // components it works with.
65 | struct Client;
66 |
67 | impl Client {
68 | fn client_code(component: &T) {
69 | println!("result: {}", component.operation())
70 | }
71 | }
72 |
73 | fn main() {
74 | let component = Rc::new(ConcreteComponent {});
75 | println!("client: i get a simple component: ");
76 | Client::client_code(component.as_ref());
77 | println!("client: now I've got a decorated component:");
78 | let decorator_a1 = ConcreteDecoratorA::new(component.clone());
79 | Client::client_code(&decorator_a1);
80 |
81 | let decorator_a2 = ConcreteDecoratorB::new(Rc::new(decorator_a1));
82 | Client::client_code(&decorator_a2);
83 | }
84 |
--------------------------------------------------------------------------------
/structural/proxy.rs:
--------------------------------------------------------------------------------
1 | //! Proxy is a structural design pattern that lets you provide a substitute or placeholder for another object.
2 | //! A proxy controls access to the original object, allowing you to perform something either before or after the request gets through to the original object.
3 |
4 | // The Subject trait declares common operations for both RealSubject and
5 | // the Proxy. As long as the client works with RealSubject using this
6 | // interface, you'll be able to pass it a proxy instead of a real subject.
7 | trait Subject {
8 | fn request(&self);
9 | }
10 |
11 | // The RealSubject contains some core business logic. Usually, RealSubjects
12 | // are capable of doing some useful work which may also be very slow or
13 | // sensitive - e.g. correcting input data. A Proxy can solve these issues
14 | // without any changes to the RealSubject's code.
15 | struct RealSubject {}
16 | impl Subject for RealSubject {
17 | fn request(&self) {
18 | println!("RealSubject: handling request.");
19 | }
20 | }
21 |
22 | struct Proxy<'a> {
23 | real_subject: &'a RealSubject,
24 | }
25 | impl<'a> Proxy<'a> {
26 | fn new(real_subject: &'a RealSubject) -> Proxy {
27 | Proxy { real_subject }
28 | }
29 | fn check_access(&self) -> bool {
30 | // Some real checks should go here.
31 | println!("Proxy: checking access prior to firing a real request.");
32 | true
33 | }
34 | fn log_access(&self) {
35 | println!("Proxy: logging the request.");
36 | }
37 | }
38 |
39 | impl<'a> Subject for Proxy<'a> {
40 | // The most common applications of the Proxy pattern are lazy loading,
41 | // caching, controlling the access, logging, etc. A Proxy can perform one of
42 | // these things and then, depending on the result, pass the execution to the
43 | // same method in a linked RealSubject object.
44 | fn request(&self) {
45 | if self.check_access() {
46 | self.real_subject.request();
47 | self.log_access();
48 | }
49 | }
50 | }
51 |
52 | struct Client;
53 | impl Client {
54 | // The client code is supposed to work with all objects (both subjects
55 | // and proxies) via the Subject interface in order to support both real
56 | // subjects and proxies. In real life, however, clients mostly work with
57 | // their real subjects directly. In this case, to implement the pattern
58 | // more easily, you can extend your proxy from the real subject's class.
59 | fn client_code(subject: &T) {
60 | subject.request();
61 | }
62 | }
63 |
64 | fn main() {
65 | let real_subject = RealSubject {};
66 | println!("client: executing the client code with a real subject:");
67 | Client::client_code(&real_subject);
68 |
69 | println!("");
70 | println!("client: executing the same client code with a proxy:");
71 | let proxy = Proxy::new(&real_subject);
72 | Client::client_code(&proxy);
73 | }
74 |
--------------------------------------------------------------------------------