├── .gitignore ├── src ├── lib.rs ├── structural │ ├── facade │ │ ├── rock_band │ │ │ ├── mod.rs │ │ │ ├── musician │ │ │ │ ├── mod.rs │ │ │ │ ├── drummer.rs │ │ │ │ ├── vocalist.rs │ │ │ │ ├── bassist.rs │ │ │ │ └── guitarist.rs │ │ │ └── black_sabbath.rs │ │ └── mod.rs │ ├── decorator │ │ ├── pizza │ │ │ ├── mod.rs │ │ │ ├── plain_pizza.rs │ │ │ ├── mozzarella.rs │ │ │ └── tomato_sauce.rs │ │ └── mod.rs │ ├── proxy │ │ ├── mod.rs │ │ └── driver.rs │ ├── flyweight │ │ ├── mod.rs │ │ └── parking │ │ │ ├── car │ │ │ ├── car_type.rs │ │ │ ├── mod.rs │ │ │ └── car_factory.rs │ │ │ └── mod.rs │ ├── bridge │ │ ├── mod.rs │ │ └── transportation.rs │ ├── adapter │ │ ├── mod.rs │ │ └── chief.rs │ ├── composite │ │ ├── mod.rs │ │ └── roman_army │ │ │ ├── mod.rs │ │ │ └── primitives.rs │ └── mod.rs ├── behavioral │ ├── chain_of_responsibility │ │ ├── car │ │ │ ├── mod.rs │ │ │ ├── garage.rs │ │ │ └── handler.rs │ │ ├── mod.rs │ │ └── police_department.rs │ ├── command │ │ ├── mod.rs │ │ └── cohort.rs │ ├── mod.rs │ └── interpreter │ │ ├── mod.rs │ │ └── shop_assistant.rs └── creational │ ├── prototype │ ├── mod.rs │ └── cookie_machine.rs │ ├── factory_method │ ├── mod.rs │ ├── report │ │ ├── mod.rs │ │ ├── html.rs │ │ └── markdown.rs │ └── toy.rs │ ├── mod.rs │ ├── abstract_factory │ ├── launch │ │ ├── launch_factory.rs │ │ ├── drink.rs │ │ ├── main_course.rs │ │ └── mod.rs │ └── mod.rs │ ├── builder │ ├── mod.rs │ ├── config.rs │ └── report.rs │ └── singleton │ └── mod.rs ├── .vscode └── extensions.json ├── Cargo.toml ├── .editorconfig ├── .devcontainer ├── Dockerfile └── devcontainer.json ├── examples ├── structural_facade_rock_band.rs ├── structural_adapter_chief.rs ├── behavioral_command_cohort.rs ├── creational_factory_method_toy.rs ├── structural_proxy_driver.rs ├── creational_abstract_factory_launch.rs ├── behavioral_interpreter_shop_assistant.rs ├── behavioral_chain_of_responsibility_car.rs ├── structural_decorator_pizza.rs ├── creational_builder_config.rs ├── structural_bridge_transportation.rs ├── structural_flyweight_parking.rs ├── creational_prototype_cookie_machine.rs ├── creational_factory_method_report.rs ├── behavioral_chain_of_responsibility_police_department.rs ├── creational_singleton.rs ├── structural_composite_roman_army.rs └── creational_builder_report.rs ├── .github ├── dependabot.yml └── workflows │ ├── rust.yml │ └── rust-clippy.yml ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod behavioral; 2 | pub mod creational; 3 | pub mod structural; 4 | -------------------------------------------------------------------------------- /src/structural/facade/rock_band/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod black_sabbath; 2 | pub mod musician; 3 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "rust-lang.rust-analyzer" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "design-patterns" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | regex = "1" 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | indent_style = space 7 | indent_size = 4 8 | 9 | [{*.json,*.md,*.yml}] 10 | indent_size = 2 11 | -------------------------------------------------------------------------------- /src/structural/facade/rock_band/musician/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bassist; 2 | pub mod drummer; 3 | pub mod guitarist; 4 | pub mod vocalist; 5 | 6 | pub trait Musician { 7 | fn output(&self, text: &str); 8 | } 9 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # [Choice] Debian OS version (use bullseye on local arm64/Apple Silicon): buster, bullseye 2 | ARG VARIANT="buster" 3 | FROM mcr.microsoft.com/vscode/devcontainers/rust:1-${VARIANT} 4 | -------------------------------------------------------------------------------- /src/structural/decorator/pizza/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod mozzarella; 2 | pub mod plain_pizza; 3 | pub mod tomato_sauce; 4 | 5 | pub trait Pizza { 6 | fn get_description(&self) -> String; 7 | fn get_cost(&self) -> f32; 8 | } 9 | -------------------------------------------------------------------------------- /examples/structural_facade_rock_band.rs: -------------------------------------------------------------------------------- 1 | use design_patterns::structural::facade::rock_band::black_sabbath::BlackSabbath; 2 | 3 | fn main() { 4 | let black_sabbath = BlackSabbath::new(); 5 | black_sabbath.play_cool_song(); 6 | } 7 | -------------------------------------------------------------------------------- /src/structural/proxy/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Proxy 2 | //! 3 | //! ## Type 4 | //! 5 | //! Structural 6 | //! 7 | //! ## Description 8 | //! 9 | //! Provide a surrogate or placeholder for another object to control access to it. 10 | 11 | pub mod driver; 12 | -------------------------------------------------------------------------------- /src/structural/flyweight/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Flyweight 2 | //! 3 | //! ## Type 4 | //! 5 | //! Structural 6 | //! 7 | //! ## Description 8 | //! 9 | //! Use sharing to support large numbers of fine grained objects efficiently. 10 | 11 | pub mod parking; 12 | -------------------------------------------------------------------------------- /src/structural/bridge/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Bridge 2 | //! 3 | //! ## Type 4 | //! 5 | //! Structural 6 | //! 7 | //! ## Description 8 | //! 9 | //! Decouple an abstraction from its implementation so that the two can vary independently. 10 | 11 | pub mod transportation; 12 | -------------------------------------------------------------------------------- /examples/structural_adapter_chief.rs: -------------------------------------------------------------------------------- 1 | use design_patterns::structural::adapter::chief::{Chief, ChiefAdapter}; 2 | 3 | fn main() { 4 | let chief = ChiefAdapter::new(); 5 | let price = chief.get_cost(); 6 | chief.make_dinner(); 7 | chief.take_money(price + 5); 8 | } 9 | -------------------------------------------------------------------------------- /src/behavioral/chain_of_responsibility/car/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod garage; 2 | pub mod handler; 3 | 4 | pub struct Car { 5 | km: u32, 6 | fuel: u8, 7 | oil: u8, 8 | } 9 | 10 | impl Car { 11 | pub fn new(km: u32, fuel: u8, oil: u8) -> Car { 12 | Car { km, fuel, oil } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/creational/prototype/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Prototype 2 | //! 3 | //! ## Type 4 | //! 5 | //! Creational 6 | //! 7 | //! ## Description 8 | //! 9 | //! Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype. 10 | 11 | pub mod cookie_machine; 12 | -------------------------------------------------------------------------------- /src/structural/decorator/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Decorator 2 | //! 3 | //! ## Type 4 | //! 5 | //! Structural 6 | //! 7 | //! ## Description 8 | //! 9 | //! Attach additional responsibilities to an object dynamically. 10 | //! Provide a flexible alternative to sub-classing for extending functionality. 11 | 12 | pub mod pizza; 13 | -------------------------------------------------------------------------------- /src/structural/facade/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Facade 2 | //! 3 | //! ## Type 4 | //! 5 | //! Structural 6 | //! 7 | //! ## Description 8 | //! 9 | //! Provide a unified interface to a set of interfaces in a subsystem. 10 | //! Defines a highlevel interface that makes the subsystem easier to use. 11 | 12 | pub mod rock_band; 13 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Please see the documentation for all configuration options: 2 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: "cargo" 7 | directory: "/" 8 | schedule: 9 | interval: "daily" 10 | -------------------------------------------------------------------------------- /src/structural/adapter/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Adapter 2 | //! 3 | //! ## Type 4 | //! 5 | //! Structural 6 | //! 7 | //! ## Description 8 | //! 9 | //! Convert the interface of a class into another interface clients expect. 10 | //! Lets classes work together that couldn't otherwise because of incompatible interfaces. 11 | 12 | pub mod chief; 13 | -------------------------------------------------------------------------------- /src/structural/composite/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Composite 2 | //! 3 | //! ## Type 4 | //! 5 | //! Structural 6 | //! 7 | //! ## Description 8 | //! 9 | //! Compose objects into tree structures to represent part-whole hierarchies. 10 | //! Lets clients treat individual objects and compositions of objects uniformly. 11 | 12 | pub mod roman_army; 13 | -------------------------------------------------------------------------------- /src/behavioral/command/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Command 2 | //! 3 | //! ## Type 4 | //! 5 | //! Behavioral 6 | //! 7 | //! ## Description 8 | //! 9 | //! Encapsulate a request as an object, thereby letting you parameterize 10 | //! clients with different requests, queue or log requests, 11 | //! and support undoable operations. 12 | 13 | pub mod cohort; 14 | -------------------------------------------------------------------------------- /src/creational/factory_method/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Factory Method 2 | //! 3 | //! ## Type 4 | //! 5 | //! Creational 6 | //! 7 | //! ## Description 8 | //! 9 | //! Define an interface for creating an object, but let subclasses decide 10 | //! which class to instantiate. Lets a class defer instantiation to subclasses. 11 | 12 | pub mod report; 13 | pub mod toy; 14 | -------------------------------------------------------------------------------- /examples/behavioral_command_cohort.rs: -------------------------------------------------------------------------------- 1 | use design_patterns::behavioral::command::cohort::{ 2 | AttackCommand, Cohort, CohortCommander, RetreatCommand, 3 | }; 4 | 5 | fn main() { 6 | let mut commander = CohortCommander::new(Cohort {}, AttackCommand {}, RetreatCommand {}); 7 | commander.attack(); 8 | commander.stop(); 9 | commander.retreat(); 10 | commander.stop(); 11 | } 12 | -------------------------------------------------------------------------------- /examples/creational_factory_method_toy.rs: -------------------------------------------------------------------------------- 1 | use design_patterns::creational::factory_method::toy::{ 2 | DollFactory, RobotFactory, Toy, ToyFactory, 3 | }; 4 | 5 | fn main() { 6 | let doll = DollFactory.create("Alena"); 7 | let robot = RobotFactory.create("Max"); 8 | 9 | play(doll); 10 | play(robot); 11 | } 12 | 13 | fn play(toy: T) { 14 | toy.sing(); 15 | toy.say_name(); 16 | } 17 | -------------------------------------------------------------------------------- /examples/structural_proxy_driver.rs: -------------------------------------------------------------------------------- 1 | use design_patterns::structural::proxy::driver::{Car, Driver, ProxyCar}; 2 | 3 | fn main() { 4 | println!("Case 1. 16 years old driver."); 5 | let car = ProxyCar::new(Driver::new(16)); 6 | car.drive_car(); 7 | 8 | println!(); 9 | 10 | println!("Case 1. 21 years old driver."); 11 | let car = ProxyCar::new(Driver::new(21)); 12 | car.drive_car(); 13 | } 14 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /examples/creational_abstract_factory_launch.rs: -------------------------------------------------------------------------------- 1 | use design_patterns::creational::abstract_factory::launch::{ 2 | BusinessLaunchFactory, CheapLaunchFactory, Launch, LaunchFactory, 3 | }; 4 | 5 | fn main() { 6 | let cheap_launch = CheapLaunchFactory.create(); 7 | cheap_launch.print_prices(&cheap_launch); 8 | 9 | let business_launch = BusinessLaunchFactory.create(); 10 | business_launch.print_prices(&business_launch); 11 | } 12 | -------------------------------------------------------------------------------- /src/creational/prototype/cookie_machine.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone)] 2 | pub struct Cookie { 3 | pub name: &'static str, 4 | pub weight: i16, 5 | } 6 | 7 | pub struct CookieMachine { 8 | cookie: Cookie, 9 | } 10 | 11 | impl CookieMachine { 12 | pub fn new(cookie: Cookie) -> CookieMachine { 13 | CookieMachine { cookie } 14 | } 15 | 16 | pub fn make_cookie(&self) -> Cookie { 17 | self.cookie.clone() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/behavioral_interpreter_shop_assistant.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use design_patterns::behavioral::interpreter::shop_assistant::Order; 3 | 4 | fn main() -> Result<(), Box> { 5 | let order = Order::parse("order x100 'pack of toothpicks' from Tesco")?; 6 | println!("{:#?}", order); 7 | 8 | let order = Order::parse("order x5 'pack of milk' from Macro")?; 9 | println!("{:#?}", order); 10 | 11 | Ok(()) 12 | } 13 | -------------------------------------------------------------------------------- /src/behavioral/chain_of_responsibility/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Chain of Responsibility 2 | //! 3 | //! ## Type 4 | //! 5 | //! Behavioral 6 | //! 7 | //! ## Description 8 | //! 9 | //! Avoid coupling the sender of a request to its receiver by giving more than 10 | //! one object a chance to handle the request. Chain the receiving objects and 11 | //! pass the request along the chain until an object handles it. 12 | 13 | pub mod car; 14 | pub mod police_department; 15 | -------------------------------------------------------------------------------- /src/structural/flyweight/parking/car/car_type.rs: -------------------------------------------------------------------------------- 1 | pub struct CarType { 2 | pub body: Body, 3 | pub colour: Colour, 4 | } 5 | 6 | impl CarType { 7 | pub fn new(body: Body, colour: Colour) -> CarType { 8 | CarType { body, colour } 9 | } 10 | } 11 | 12 | #[derive(Debug, PartialEq)] 13 | pub enum Body { 14 | Sedan, 15 | Coupe, 16 | Truck, 17 | } 18 | 19 | #[derive(Debug, PartialEq)] 20 | pub enum Colour { 21 | Black, 22 | Yellow, 23 | Red, 24 | } 25 | -------------------------------------------------------------------------------- /examples/behavioral_chain_of_responsibility_car.rs: -------------------------------------------------------------------------------- 1 | use design_patterns::behavioral::chain_of_responsibility::car::{ 2 | garage::Garage, 3 | handler::{HandleFuel, HandleKm, HandleOil}, 4 | Car, 5 | }; 6 | 7 | fn main() { 8 | let mut car = Car::new(11100, 1, 3); 9 | 10 | let mut garage = Garage::new(); 11 | 12 | garage.add_handler(HandleFuel {}); 13 | garage.add_handler(HandleOil {}); 14 | garage.add_handler(HandleKm {}); 15 | 16 | garage.handle_car(&mut car); 17 | } 18 | -------------------------------------------------------------------------------- /examples/structural_decorator_pizza.rs: -------------------------------------------------------------------------------- 1 | use design_patterns::structural::decorator::pizza::{ 2 | mozzarella::Mozzarella, plain_pizza::PlainPizza, tomato_sauce::TomatoSauce, Pizza, 3 | }; 4 | 5 | fn main() { 6 | let plain_pizza = PlainPizza::new(); 7 | let mozzarella = Mozzarella::new(&plain_pizza); 8 | let tomato_sauce = TomatoSauce::new(&mozzarella); 9 | 10 | println!("Description: {}", tomato_sauce.get_description()); 11 | println!("Cost: {}", tomato_sauce.get_cost()); 12 | } 13 | -------------------------------------------------------------------------------- /src/creational/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Creational 2 | //! 3 | //! Responsible for efficient object creation mechanisms, 4 | //! which increase the flexibility and reuse of existing code. 5 | //! 6 | //! 1. [Abstract Factory](abstract_factory) 7 | //! 1. [Builder](builder) 8 | //! 1. [Factory Method](factory_method) 9 | //! 1. [Prototype](prototype) 10 | //! 1. [Singleton](singleton) 11 | 12 | pub mod abstract_factory; 13 | pub mod builder; 14 | pub mod factory_method; 15 | pub mod prototype; 16 | pub mod singleton; 17 | -------------------------------------------------------------------------------- /examples/creational_builder_config.rs: -------------------------------------------------------------------------------- 1 | use design_patterns::creational::builder::config::ConfigBuilder; 2 | 3 | /// Example with *Builder* pattern 4 | fn main() { 5 | let config = ConfigBuilder::new() 6 | .with_width(150) 7 | .with_title("Rust is awesome") 8 | .finalize(); 9 | println!("{config}"); 10 | 11 | let config = ConfigBuilder::new() 12 | .with_width(1000) 13 | .with_height(750) 14 | .with_minimized(true) 15 | .finalize(); 16 | println!("{config}"); 17 | } 18 | -------------------------------------------------------------------------------- /src/structural/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Structural 2 | //! 3 | //! Responsible for building simple and efficient class hierarchies and relations between different classes. 4 | //! 5 | //! 1. [Adapter](adapter) 6 | //! 1. [Bridge](bridge) 7 | //! 1. [Composite](composite) 8 | //! 1. [Decorator](decorator) 9 | //! 1. [Facade](facade) 10 | //! 1. [Flyweight](flyweight) 11 | //! 1. [Proxy](proxy) 12 | 13 | pub mod adapter; 14 | pub mod bridge; 15 | pub mod composite; 16 | pub mod decorator; 17 | pub mod facade; 18 | pub mod flyweight; 19 | pub mod proxy; 20 | -------------------------------------------------------------------------------- /src/behavioral/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Behavioral 2 | //! 3 | //! Behavioral patterns are responsible for the efficient and safe distribution of behaviors among the program's objects. 4 | //! 5 | //! 1. [Chain of Responsibility](chain_of_responsibility) 6 | //! 1. [Command](command) 7 | //! 1. [Interpreter](interpreter) 8 | //! 1. Iterator 9 | //! 1. Mediator 10 | //! 1. Memento 11 | //! 1. Observer 12 | //! 1. State 13 | //! 1. Strategy 14 | //! 1. Template Method 15 | //! 1. Visitor 16 | 17 | pub mod chain_of_responsibility; 18 | pub mod command; 19 | pub mod interpreter; 20 | -------------------------------------------------------------------------------- /src/creational/abstract_factory/launch/launch_factory.rs: -------------------------------------------------------------------------------- 1 | use super::{BusinessLaunch, CheapLaunch}; 2 | 3 | pub trait LaunchFactory { 4 | fn create(&self) -> T; 5 | } 6 | 7 | pub struct CheapLaunchFactory; 8 | pub struct BusinessLaunchFactory; 9 | 10 | impl LaunchFactory for CheapLaunchFactory { 11 | fn create(&self) -> CheapLaunch { 12 | CheapLaunch::new() 13 | } 14 | } 15 | 16 | impl LaunchFactory for BusinessLaunchFactory { 17 | fn create(&self) -> BusinessLaunch { 18 | BusinessLaunch::new() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/structural/decorator/pizza/plain_pizza.rs: -------------------------------------------------------------------------------- 1 | use super::Pizza; 2 | 3 | pub struct PlainPizza<'a> { 4 | description: &'a str, 5 | cost: f32, 6 | } 7 | 8 | impl PlainPizza<'_> { 9 | pub fn new<'a>() -> PlainPizza<'a> { 10 | PlainPizza { 11 | description: "Plain pizza", 12 | cost: 5.0, 13 | } 14 | } 15 | } 16 | 17 | impl Pizza for PlainPizza<'_> { 18 | fn get_description(&self) -> String { 19 | self.description.to_string() 20 | } 21 | 22 | fn get_cost(&self) -> f32 { 23 | self.cost 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/structural/facade/rock_band/musician/drummer.rs: -------------------------------------------------------------------------------- 1 | use super::Musician; 2 | 3 | pub struct Drummer { 4 | name: String, 5 | } 6 | 7 | impl Drummer { 8 | pub fn new>(name: S) -> Drummer { 9 | Drummer { name: name.into() } 10 | } 11 | 12 | pub fn start_playing(&self) { 13 | self.output("start playing"); 14 | } 15 | 16 | pub fn stop_playing(&self) { 17 | self.output("stop playing"); 18 | } 19 | } 20 | 21 | impl Musician for Drummer { 22 | fn output(&self, text: &str) { 23 | println!("{} {text}.", self.name); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/behavioral/interpreter/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Interpreter 2 | //! 3 | //! ## Type 4 | //! 5 | //! Behavioral 6 | //! 7 | //! ## Description 8 | //! 9 | //! In computer programming, the interpreter pattern is a design pattern that 10 | //! specifies how to evaluate sentences in a language. The basic idea is to 11 | //! have a class for each symbol (terminal or nonterminal) in a specialized 12 | //! computer language. The syntax tree of a sentence in the language is an 13 | //! instance of the composite pattern and is used to evaluate (interpret) the 14 | //! sentence for a client. 15 | 16 | pub mod shop_assistant; 17 | -------------------------------------------------------------------------------- /src/creational/abstract_factory/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Abstract Factory 2 | //! 3 | //! ## Type 4 | //! 5 | //! Creational 6 | //! 7 | //! ## Description 8 | //! 9 | //! Provides an interface for creating families of related or dependent objects without specifying their concrete class. 10 | //! 11 | //! ## Addition 12 | //! 13 | //! ### Families of objects 14 | //! 15 | //! For instance, take this set of classes: `Transport` + `Engine` + `Controls`. There are might be several variants of these: 16 | //! 17 | //! 1. `Car` + `CombustionEngine` + `SteeringWheel` 18 | //! 1. `Plane` + `JetEngine` + `Yoke` 19 | 20 | pub mod launch; 21 | -------------------------------------------------------------------------------- /src/creational/builder/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Builder 2 | //! 3 | //! ## Type 4 | //! 5 | //! Creational pattern 6 | //! 7 | //! ## Description 8 | //! 9 | //! Separate the construction of a complex object from its representing 10 | //! so that the same construction process can create different representations. 11 | //! 12 | //! ## Addition 13 | //! 14 | //! ### Complex object 15 | //! 16 | //! There are might be several variants of coffee recipes: 17 | //! 18 | //! 1. `coffee` + `milk` + `cocoa` + `sugar` 19 | //! 1. `coffee` + `sugar` 20 | //! 1. `coffee` + `sugar` + `sugar` + `milk` 21 | 22 | pub mod config; 23 | pub mod report; 24 | -------------------------------------------------------------------------------- /src/structural/facade/rock_band/musician/vocalist.rs: -------------------------------------------------------------------------------- 1 | use super::Musician; 2 | 3 | pub struct Vocalist { 4 | name: String, 5 | } 6 | 7 | impl Vocalist { 8 | pub fn new>(name: S) -> Vocalist { 9 | Vocalist { name: name.into() } 10 | } 11 | 12 | pub fn sing_couplet(&self, couplet_number: i32) { 13 | self.output(&format!("singing a couplet № {couplet_number}")); 14 | } 15 | 16 | pub fn sing_chorus(&self) { 17 | self.output("singing a chorus"); 18 | } 19 | } 20 | 21 | impl Musician for Vocalist { 22 | fn output(&self, text: &str) { 23 | println!("{} {text}.", self.name); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/structural/decorator/pizza/mozzarella.rs: -------------------------------------------------------------------------------- 1 | use super::Pizza; 2 | 3 | pub struct Mozzarella<'a> { 4 | base: &'a dyn Pizza, 5 | description: &'a str, 6 | cost: f32, 7 | } 8 | 9 | impl<'a> Mozzarella<'a> { 10 | pub fn new(base: &'a T) -> Mozzarella<'a> { 11 | Mozzarella { 12 | base, 13 | description: "Mozzarella", 14 | cost: 2.49, 15 | } 16 | } 17 | } 18 | 19 | impl<'a> Pizza for Mozzarella<'a> { 20 | fn get_description(&self) -> String { 21 | format!("{}, {}", self.base.get_description(), &self.description) 22 | } 23 | 24 | fn get_cost(&self) -> f32 { 25 | self.base.get_cost() + self.cost 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/structural/decorator/pizza/tomato_sauce.rs: -------------------------------------------------------------------------------- 1 | use super::Pizza; 2 | 3 | pub struct TomatoSauce<'a> { 4 | base: &'a dyn Pizza, 5 | description: &'a str, 6 | cost: f32, 7 | } 8 | 9 | impl<'a> TomatoSauce<'a> { 10 | pub fn new(base: &'a T) -> TomatoSauce<'a> { 11 | TomatoSauce { 12 | base, 13 | description: "Tomato Sauce", 14 | cost: 0.75, 15 | } 16 | } 17 | } 18 | 19 | impl<'a> Pizza for TomatoSauce<'a> { 20 | fn get_description(&self) -> String { 21 | format!("{}, {}", self.base.get_description(), &self.description) 22 | } 23 | 24 | fn get_cost(&self) -> f32 { 25 | self.base.get_cost() + self.cost 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/structural_bridge_transportation.rs: -------------------------------------------------------------------------------- 1 | use design_patterns::structural::bridge::transportation::{ 2 | delivery::{Express, Normal}, 3 | transport::{Plane, Train, Transport}, 4 | }; 5 | 6 | fn main() { 7 | let mut plane = Plane::new(Box::new(Express {})); 8 | plane.load(); 9 | plane.carry(); 10 | plane.unload(); 11 | 12 | plane.set_delivery(Box::new(Normal {})); 13 | plane.load(); 14 | plane.carry(); 15 | plane.unload(); 16 | 17 | let mut train = Train::new(Box::new(Express {})); 18 | train.load(); 19 | train.carry(); 20 | train.unload(); 21 | 22 | train.set_delivery(Box::new(Normal {})); 23 | train.load(); 24 | train.carry(); 25 | train.unload(); 26 | } 27 | -------------------------------------------------------------------------------- /src/behavioral/chain_of_responsibility/car/garage.rs: -------------------------------------------------------------------------------- 1 | use super::handler::Handler; 2 | use super::Car; 3 | 4 | pub struct Garage { 5 | handlers: Vec>, 6 | } 7 | 8 | impl Garage { 9 | pub fn new() -> Garage { 10 | Garage { 11 | handlers: Vec::new(), 12 | } 13 | } 14 | 15 | pub fn add_handler(&mut self, handler: impl Handler + 'static) { 16 | self.handlers.push(Box::new(handler)); 17 | } 18 | 19 | pub fn handle_car(&self, car: &mut Car) { 20 | for handle in &self.handlers { 21 | handle.handle(car); 22 | } 23 | } 24 | } 25 | 26 | impl Default for Garage { 27 | fn default() -> Self { 28 | Self::new() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/structural/flyweight/parking/car/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod car_factory; 2 | pub mod car_type; 3 | 4 | use car_type::CarType; 5 | 6 | pub struct Car { 7 | pub license_plate: String, 8 | pub parking_place_number: u8, 9 | pub car_type_id: u8, 10 | } 11 | 12 | impl Car { 13 | pub fn new(license_plate: String, parking_place_number: u8, car_type_id: u8) -> Car { 14 | Car { 15 | license_plate, 16 | parking_place_number, 17 | car_type_id, 18 | } 19 | } 20 | 21 | pub fn print(&self, car_type: &CarType) { 22 | println!( 23 | "{} - {:?} {:?}, {}", 24 | self.parking_place_number, car_type.colour, car_type.body, self.license_plate 25 | ) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/structural/facade/rock_band/musician/bassist.rs: -------------------------------------------------------------------------------- 1 | use super::Musician; 2 | 3 | pub struct Bassist { 4 | name: String, 5 | } 6 | 7 | impl Bassist { 8 | pub fn new>(name: S) -> Bassist { 9 | Bassist { name: name.into() } 10 | } 11 | 12 | pub fn follow_the_drums(&self) { 13 | self.output("follow the drums"); 14 | } 15 | 16 | pub fn change_rhythm(&self, rhythm_type: &str) { 17 | self.output(&format!("switched to the rhythm of the {rhythm_type}")); 18 | } 19 | 20 | pub fn stop_playing(&self) { 21 | self.output("stop playing"); 22 | } 23 | } 24 | 25 | impl Musician for Bassist { 26 | fn output(&self, text: &str) { 27 | println!("{} {text}.", self.name); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/creational/factory_method/report/mod.rs: -------------------------------------------------------------------------------- 1 | mod html; 2 | mod markdown; 3 | 4 | pub use html::HtmlReport; 5 | pub use markdown::MarkdownReport; 6 | 7 | pub trait Report { 8 | fn set_header>(&mut self, header: S); 9 | fn set_content>(&mut self, content: S); 10 | } 11 | 12 | pub trait ReportFactory { 13 | fn new_report(&self) -> R; 14 | } 15 | 16 | pub struct MarkdownReportFactory; 17 | pub struct HtmlReportFactory; 18 | 19 | impl ReportFactory for MarkdownReportFactory { 20 | fn new_report(&self) -> MarkdownReport { 21 | MarkdownReport::new() 22 | } 23 | } 24 | 25 | impl ReportFactory for HtmlReportFactory { 26 | fn new_report(&self) -> HtmlReport { 27 | HtmlReport::new() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/structural_flyweight_parking.rs: -------------------------------------------------------------------------------- 1 | use design_patterns::structural::flyweight::parking::{ 2 | car::car_type::{Body, Colour}, 3 | Parking, 4 | }; 5 | 6 | fn main() { 7 | use Body::{Coupe, Sedan, Truck}; 8 | use Colour::{Black, Red, Yellow}; 9 | 10 | let mut parking = Parking::new(); 11 | parking.add_car("NFS MW", 13, Coupe, Red); 12 | parking.add_car("MW2017", 15, Truck, Yellow); 13 | parking.add_car("BIG11", 37, Sedan, Black); 14 | parking.add_car("KING", 64, Coupe, Red); 15 | parking.add_car("MAN", 73, Coupe, Red); 16 | parking.add_car("TE64G", 18, Truck, Yellow); 17 | parking.add_car("SMILE", 39, Sedan, Black); 18 | parking.add_car("DARK01", 24, Truck, Black); 19 | parking.add_car("DARK03", 25, Truck, Black); 20 | parking.add_car("DARK05", 26, Truck, Black); 21 | 22 | parking.print(); 23 | } 24 | -------------------------------------------------------------------------------- /examples/creational_prototype_cookie_machine.rs: -------------------------------------------------------------------------------- 1 | use design_patterns::creational::prototype::cookie_machine::{Cookie, CookieMachine}; 2 | 3 | fn main() { 4 | let coconut_cookie = Cookie { 5 | name: "coconut", 6 | weight: 16, 7 | }; 8 | let coconut_cookie_machine = CookieMachine::new(coconut_cookie); 9 | let cloned_cookie = coconut_cookie_machine.make_cookie(); 10 | println!( 11 | "{} clone with weight {}\n", 12 | cloned_cookie.name, cloned_cookie.weight 13 | ); 14 | 15 | let chocolate_cookie = Cookie { 16 | name: "chocolate", 17 | weight: 28, 18 | }; 19 | let chocolate_cookie_machine = CookieMachine::new(chocolate_cookie); 20 | let chocolate_cookies = vec![chocolate_cookie_machine.make_cookie(); 6]; 21 | 22 | for cookie in chocolate_cookies { 23 | println!("{} clone with weight {}", cookie.name, cookie.weight); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/behavioral/chain_of_responsibility/car/handler.rs: -------------------------------------------------------------------------------- 1 | use super::Car; 2 | 3 | pub trait Handler { 4 | fn handle(&self, car: &mut Car); 5 | } 6 | 7 | pub struct HandleFuel; 8 | 9 | impl Handler for HandleFuel { 10 | fn handle(&self, car: &mut Car) { 11 | if car.fuel < 10 { 12 | println!("The car is refueled"); 13 | car.fuel = 100; 14 | } 15 | } 16 | } 17 | 18 | pub struct HandleKm; 19 | 20 | impl Handler for HandleKm { 21 | fn handle(&self, car: &mut Car) { 22 | if car.km > 10000 { 23 | println!("A technical inspection of the car was made"); 24 | car.km = 100; 25 | } 26 | } 27 | } 28 | 29 | pub struct HandleOil; 30 | 31 | impl Handler for HandleOil { 32 | fn handle(&self, car: &mut Car) { 33 | if car.oil < 10 { 34 | println!("Added oil"); 35 | car.oil = 100; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/creational/factory_method/report/html.rs: -------------------------------------------------------------------------------- 1 | use super::Report; 2 | use std::fmt; 3 | 4 | pub struct HtmlReport { 5 | header: String, 6 | content: String, 7 | } 8 | 9 | impl HtmlReport { 10 | pub fn new() -> HtmlReport { 11 | HtmlReport { 12 | header: String::new(), 13 | content: String::new(), 14 | } 15 | } 16 | } 17 | 18 | impl Default for HtmlReport { 19 | fn default() -> Self { 20 | Self::new() 21 | } 22 | } 23 | 24 | impl Report for HtmlReport { 25 | fn set_header>(&mut self, header: S) { 26 | self.header = header.into(); 27 | } 28 | fn set_content>(&mut self, content: S) { 29 | self.content = content.into(); 30 | } 31 | } 32 | 33 | impl fmt::Display for HtmlReport { 34 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 35 | write!(f, "

{}

\n

{}

\n", self.header, self.content) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/creational/abstract_factory/launch/drink.rs: -------------------------------------------------------------------------------- 1 | pub trait Drink { 2 | fn get_price(&self) -> f32; 3 | fn get_volume(&self) -> u32; 4 | } 5 | 6 | #[derive(Clone, Copy)] 7 | pub struct CheapDrink { 8 | volume: u32, 9 | } 10 | 11 | impl CheapDrink { 12 | pub fn new(volume: u32) -> CheapDrink { 13 | CheapDrink { volume } 14 | } 15 | } 16 | 17 | impl Drink for CheapDrink { 18 | fn get_price(&self) -> f32 { 19 | 4.95 20 | } 21 | 22 | fn get_volume(&self) -> u32 { 23 | self.volume 24 | } 25 | } 26 | 27 | #[derive(Clone, Copy)] 28 | pub struct BusinessDrink { 29 | volume: u32, 30 | } 31 | 32 | impl BusinessDrink { 33 | pub fn new(volume: u32) -> BusinessDrink { 34 | BusinessDrink { volume } 35 | } 36 | } 37 | 38 | impl Drink for BusinessDrink { 39 | fn get_price(&self) -> f32 { 40 | 9.95 41 | } 42 | 43 | fn get_volume(&self) -> u32 { 44 | self.volume 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/creational/factory_method/report/markdown.rs: -------------------------------------------------------------------------------- 1 | use super::Report; 2 | use std::fmt; 3 | 4 | pub struct MarkdownReport { 5 | header: String, 6 | content: String, 7 | } 8 | 9 | impl MarkdownReport { 10 | pub fn new() -> MarkdownReport { 11 | MarkdownReport { 12 | header: String::new(), 13 | content: String::new(), 14 | } 15 | } 16 | } 17 | 18 | impl Default for MarkdownReport { 19 | fn default() -> Self { 20 | Self::new() 21 | } 22 | } 23 | 24 | impl Report for MarkdownReport { 25 | fn set_header>(&mut self, header: S) { 26 | self.header = header.into(); 27 | } 28 | fn set_content>(&mut self, content: S) { 29 | self.content = content.into(); 30 | } 31 | } 32 | 33 | impl fmt::Display for MarkdownReport { 34 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 35 | write!(f, "# {}\n\n{}\n", self.header, self.content) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/structural/facade/rock_band/musician/guitarist.rs: -------------------------------------------------------------------------------- 1 | use super::Musician; 2 | 3 | pub struct Guitarist { 4 | name: String, 5 | } 6 | 7 | impl Guitarist { 8 | pub fn new>(name: S) -> Guitarist { 9 | Guitarist { name: name.into() } 10 | } 11 | 12 | pub fn play_cool_opening(&self) { 13 | self.output("starting with a steep entry"); 14 | } 15 | 16 | pub fn play_cool_riffs(&self) { 17 | self.output("playing cool riffs"); 18 | } 19 | 20 | pub fn play_another_cool_riffs(&self) { 21 | self.output("playing another cool riffs"); 22 | } 23 | 24 | pub fn play_incredibly_cool_solo(&self) { 25 | self.output("playing incredibly cool solo"); 26 | } 27 | 28 | pub fn play_final_accord(&self) { 29 | self.output("playing final accord"); 30 | } 31 | } 32 | 33 | impl Musician for Guitarist { 34 | fn output(&self, text: &str) { 35 | println!("{} {text}.", self.name); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/creational/abstract_factory/launch/main_course.rs: -------------------------------------------------------------------------------- 1 | pub trait MainCourse { 2 | fn get_price(&self) -> f32; 3 | fn get_weight(&self) -> u32; 4 | } 5 | 6 | #[derive(Clone, Copy)] 7 | pub struct CheapCourse { 8 | weight: u32, 9 | } 10 | 11 | impl CheapCourse { 12 | pub fn new(weight: u32) -> CheapCourse { 13 | CheapCourse { weight } 14 | } 15 | } 16 | 17 | impl MainCourse for CheapCourse { 18 | fn get_price(&self) -> f32 { 19 | 9.99 20 | } 21 | 22 | fn get_weight(&self) -> u32 { 23 | self.weight 24 | } 25 | } 26 | 27 | #[derive(Clone, Copy)] 28 | pub struct BusinessCourse { 29 | weight: u32, 30 | } 31 | 32 | impl BusinessCourse { 33 | pub fn new(weight: u32) -> BusinessCourse { 34 | BusinessCourse { weight } 35 | } 36 | } 37 | 38 | impl MainCourse for BusinessCourse { 39 | fn get_price(&self) -> f32 { 40 | 19.99 41 | } 42 | 43 | fn get_weight(&self) -> u32 { 44 | self.weight 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /examples/creational_factory_method_report.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Display; 2 | 3 | use design_patterns::creational::factory_method::report::{ 4 | HtmlReportFactory, MarkdownReportFactory, Report, ReportFactory, 5 | }; 6 | 7 | fn main() { 8 | let markdown_factory = MarkdownReportFactory; 9 | let html_factory = HtmlReportFactory; 10 | 11 | let markdown_report = markdown_factory.new_report(); 12 | let html_report = html_factory.new_report(); 13 | 14 | let header = "The Unfaithful Friend"; 15 | let content = "There was a large berry tree on the bank of a river. \ 16 | On this tree lived a monkey called Rhesa. He was a clever and good hearted monkey..."; 17 | 18 | print_report(markdown_report, header, content); 19 | 20 | print_report(html_report, header, content); 21 | } 22 | 23 | fn print_report(mut report: T, header: &'static str, content: &'static str) { 24 | report.set_header(header); 25 | report.set_content(content); 26 | 27 | println!("{}", report); 28 | } 29 | -------------------------------------------------------------------------------- /src/structural/proxy/driver.rs: -------------------------------------------------------------------------------- 1 | pub struct Driver { 2 | age: u8, 3 | } 4 | 5 | impl Driver { 6 | pub fn new(age: u8) -> Driver { 7 | Driver { age } 8 | } 9 | 10 | pub fn get_age(&self) -> u8 { 11 | self.age 12 | } 13 | } 14 | 15 | pub trait Car { 16 | fn drive_car(&self); 17 | } 18 | 19 | pub struct RealCar {} 20 | 21 | impl Car for RealCar { 22 | fn drive_car(&self) { 23 | println!("Car has been driven!"); 24 | } 25 | } 26 | 27 | pub struct ProxyCar { 28 | driver: Driver, 29 | real_car: RealCar, 30 | } 31 | 32 | impl Car for ProxyCar { 33 | fn drive_car(&self) { 34 | if self.driver.age <= 16 { 35 | println!("Sorry, the driver is too young to drive.") 36 | } else { 37 | self.real_car.drive_car(); 38 | } 39 | } 40 | } 41 | 42 | impl ProxyCar { 43 | pub fn new(driver: Driver) -> ProxyCar { 44 | ProxyCar { 45 | driver, 46 | real_car: RealCar {}, 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/behavioral_chain_of_responsibility_police_department.rs: -------------------------------------------------------------------------------- 1 | use design_patterns::behavioral::chain_of_responsibility::police_department::{ 2 | Detective, Patrolman, Policeman, 3 | }; 4 | 5 | // Let's create a chain of policemen (Jack -> Tom -> Chuck). 6 | // Every officer has an individual level of deduction. 7 | // And every crime has a difficult lvl. 8 | // Officer can investigate the crime by himself if his deduction is not less 9 | // than crime's difficulty lvl. 10 | // The officer passes the crime's case if the crime's difficulty lvl bigger 11 | // than the officer's deduction. 12 | fn main() { 13 | let chuck = Detective::new(8, "Chuck"); 14 | 15 | let mut tom = Detective::new(5, "Tom"); 16 | tom.set_next(Box::new(chuck)); 17 | 18 | let mut jack = Patrolman::new(2, "Jack"); 19 | jack.set_next(Box::new(tom)); 20 | 21 | for crime_lvl in (1..=10u8).step_by(3) { 22 | println!("Investigation of a new crime (lvl: {crime_lvl})"); 23 | jack.investigate(crime_lvl); 24 | println!(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/creational_singleton.rs: -------------------------------------------------------------------------------- 1 | use design_patterns::creational::singleton; 2 | use std::{thread, time::Duration}; 3 | 4 | /// Example with *Singleton* pattern 5 | fn main() { 6 | // Let's use the singleton in a few threads 7 | let threads = (0..5) 8 | .map(|i| { 9 | thread::spawn(move || { 10 | thread::sleep(Duration::from_millis(i * 10)); 11 | let s = singleton::singleton(); 12 | let mut data = s.inner.lock().unwrap(); 13 | data.set_area(i * 15); 14 | data.set_population(12 + i); 15 | }) 16 | }) 17 | .collect::>(); 18 | 19 | // And let's check the singleton every so often 20 | for i in 0..10 { 21 | thread::sleep(Duration::from_millis(5)); 22 | 23 | let s = singleton::singleton(); 24 | let data = s.inner.lock().unwrap(); 25 | println!("{} {:?}", i, *data); 26 | } 27 | 28 | for thread in threads.into_iter() { 29 | thread.join().unwrap(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Arkadiy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/creational/factory_method/toy.rs: -------------------------------------------------------------------------------- 1 | pub trait Toy { 2 | fn sing(&self); 3 | fn say_name(&self); 4 | } 5 | 6 | pub struct Doll { 7 | pub name: String, 8 | } 9 | 10 | pub struct Robot { 11 | pub name: String, 12 | } 13 | 14 | impl Toy for Doll { 15 | fn sing(&self) { 16 | println!("Apple round, apple red..."); 17 | } 18 | 19 | fn say_name(&self) { 20 | println!("My name is {}\n", self.name); 21 | } 22 | } 23 | 24 | impl Toy for Robot { 25 | fn sing(&self) { 26 | println!("I am a robot..."); 27 | } 28 | 29 | fn say_name(&self) { 30 | println!("My name is {}\n", self.name); 31 | } 32 | } 33 | 34 | pub trait ToyFactory { 35 | fn create>(&self, name: S) -> T; 36 | } 37 | 38 | pub struct DollFactory; 39 | pub struct RobotFactory; 40 | 41 | impl ToyFactory for DollFactory { 42 | fn create>(&self, name: S) -> Doll { 43 | Doll { name: name.into() } 44 | } 45 | } 46 | 47 | impl ToyFactory for RobotFactory { 48 | fn create>(&self, name: S) -> Robot { 49 | Robot { name: name.into() } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/structural/flyweight/parking/car/car_factory.rs: -------------------------------------------------------------------------------- 1 | use super::car_type::{Body, CarType, Colour}; 2 | 3 | pub struct CarFactory { 4 | car_types: Vec, 5 | } 6 | 7 | impl CarFactory { 8 | pub fn new() -> CarFactory { 9 | CarFactory { 10 | car_types: Vec::new(), 11 | } 12 | } 13 | 14 | pub fn get_car_type_id(&mut self, body: Body, colour: Colour) -> u8 { 15 | let position = self 16 | .car_types 17 | .iter() 18 | .position(|r| r.body == body && r.colour == colour); 19 | 20 | match position { 21 | Some(x) => x as u8, 22 | None => { 23 | let car_type = CarType::new(body, colour); 24 | self.car_types.push(car_type); 25 | (self.car_types.len() - 1) as u8 26 | } 27 | } 28 | } 29 | 30 | pub fn get_car_type(&mut self, id: u8) -> Option<&CarType> { 31 | self.car_types.get(id as usize) 32 | } 33 | 34 | pub fn print(&self) { 35 | println!("Number of car types: {}", self.car_types.len()); 36 | } 37 | } 38 | 39 | impl Default for CarFactory { 40 | fn default() -> Self { 41 | Self::new() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/structural/flyweight/parking/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod car; 2 | 3 | use car::car_factory::CarFactory; 4 | use car::car_type::{Body, Colour}; 5 | use car::Car; 6 | 7 | pub struct Parking { 8 | cars: Vec, 9 | car_factory: CarFactory, 10 | } 11 | 12 | impl Parking { 13 | pub fn new() -> Parking { 14 | Parking { 15 | cars: Vec::new(), 16 | car_factory: CarFactory::new(), 17 | } 18 | } 19 | 20 | pub fn add_car( 21 | &mut self, 22 | license_plate: &str, 23 | parking_place_number: u8, 24 | body: Body, 25 | colour: Colour, 26 | ) { 27 | self.cars.push(Car::new( 28 | license_plate.to_string(), 29 | parking_place_number, 30 | self.car_factory.get_car_type_id(body, colour), 31 | )); 32 | } 33 | 34 | pub fn print(&mut self) { 35 | for car in &self.cars { 36 | let car_type = self.car_factory.get_car_type(car.car_type_id).unwrap(); 37 | car.print(car_type); 38 | } 39 | 40 | println!("\nNumber of cars: {}", self.cars.len()); 41 | self.car_factory.print(); 42 | } 43 | } 44 | 45 | impl Default for Parking { 46 | fn default() -> Self { 47 | Self::new() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/structural/composite/roman_army/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod primitives; 2 | 3 | pub trait ComponentUnit { 4 | fn get_strength(&self) -> i32; 5 | fn add_unit(&mut self, unit: Box); 6 | fn get_units(&self) -> &Vec>; 7 | fn get_unit(&self, index: usize) -> &dyn ComponentUnit; 8 | fn remove(&mut self, index: usize); 9 | } 10 | 11 | pub struct CompositeUnit { 12 | units: Vec>, 13 | } 14 | 15 | impl CompositeUnit { 16 | pub fn new() -> CompositeUnit { 17 | CompositeUnit { units: Vec::new() } 18 | } 19 | } 20 | 21 | impl Default for CompositeUnit { 22 | fn default() -> Self { 23 | Self::new() 24 | } 25 | } 26 | 27 | impl ComponentUnit for CompositeUnit { 28 | fn get_strength(&self) -> i32 { 29 | let mut res = 0; 30 | 31 | for unit in &self.units { 32 | res += unit.get_strength(); 33 | } 34 | 35 | res 36 | } 37 | 38 | fn add_unit(&mut self, unit: Box) { 39 | self.units.push(unit); 40 | } 41 | 42 | fn get_units(&self) -> &Vec> { 43 | &self.units 44 | } 45 | 46 | fn get_unit(&self, index: usize) -> &dyn ComponentUnit { 47 | &*self.units[index] 48 | } 49 | 50 | fn remove(&mut self, index: usize) { 51 | self.units.remove(index); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/structural/adapter/chief.rs: -------------------------------------------------------------------------------- 1 | pub trait Chief { 2 | fn get_cost(&self) -> u32; 3 | fn make_dinner(&self); 4 | fn take_money(&self, money: u32); 5 | } 6 | 7 | pub struct ChiefAdapter { 8 | confectioner: Confectioner, 9 | } 10 | 11 | impl ChiefAdapter { 12 | pub fn new() -> ChiefAdapter { 13 | ChiefAdapter { 14 | confectioner: Confectioner::new(), 15 | } 16 | } 17 | } 18 | 19 | impl Default for ChiefAdapter { 20 | fn default() -> Self { 21 | Self::new() 22 | } 23 | } 24 | 25 | impl Chief for ChiefAdapter { 26 | fn get_cost(&self) -> u32 { 27 | self.confectioner.get_cost_for_dinner() 28 | } 29 | 30 | fn make_dinner(&self) { 31 | self.confectioner.make_a_dinner(); 32 | } 33 | 34 | fn take_money(&self, money: u32) { 35 | self.confectioner.take_money_for_dinner(money); 36 | } 37 | } 38 | 39 | pub struct Confectioner {} 40 | 41 | impl Confectioner { 42 | pub fn new() -> Confectioner { 43 | Confectioner {} 44 | } 45 | 46 | pub fn get_cost_for_dinner(&self) -> u32 { 47 | 15 48 | } 49 | 50 | pub fn make_a_dinner(&self) { 51 | println!("Confectioner is making a dinner...\nDone!"); 52 | } 53 | 54 | pub fn take_money_for_dinner(&self, money: u32) { 55 | println!("Thanks for ${}", money); 56 | } 57 | } 58 | 59 | impl Default for Confectioner { 60 | fn default() -> Self { 61 | Self::new() 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /examples/structural_composite_roman_army.rs: -------------------------------------------------------------------------------- 1 | use design_patterns::structural::composite::roman_army::{ 2 | primitives::{Archer, Horseman, Infantryman}, 3 | ComponentUnit, CompositeUnit, 4 | }; 5 | 6 | fn main() { 7 | let mut army = CompositeUnit::new(); 8 | 9 | for _ in 0..4 { 10 | let legion = create_legion(); 11 | army.add_unit(Box::new(legion)); 12 | } 13 | 14 | { 15 | let legions = army.get_units(); 16 | println!("Roman army damaging strength is {}", army.get_strength()); 17 | println!("Roman army has {} legions\n", legions.len()); 18 | } 19 | 20 | { 21 | let legion = army.get_unit(0); 22 | println!( 23 | "Roman legion damaging strength is {}", 24 | legion.get_strength() 25 | ); 26 | println!("Roman legion has {} units\n", legion.get_units().len()); 27 | } 28 | 29 | army.remove(0); 30 | 31 | let legions = army.get_units(); 32 | println!("Roman army damaging strength is {}", army.get_strength()); 33 | println!("Roman army has {} legions", legions.len()); 34 | } 35 | 36 | fn create_legion() -> CompositeUnit { 37 | let mut composite_legion = CompositeUnit::new(); 38 | 39 | for _ in 0..3000 { 40 | composite_legion.add_unit(Box::new(Infantryman)); 41 | } 42 | 43 | for _ in 0..1200 { 44 | composite_legion.add_unit(Box::new(Archer)); 45 | } 46 | 47 | for _ in 0..300 { 48 | composite_legion.add_unit(Box::new(Horseman)); 49 | } 50 | 51 | composite_legion 52 | } 53 | -------------------------------------------------------------------------------- /examples/creational_builder_report.rs: -------------------------------------------------------------------------------- 1 | use design_patterns::creational::builder::report::{ 2 | HtmlReportBuilder, MarkdownReportBuilder, ReportBuilder, 3 | }; 4 | 5 | /// Example with *Builder* pattern 6 | fn main() { 7 | let html_report = HtmlReportBuilder::new() 8 | .with_header("Solar Deities : Hindu Mythological Story") 9 | .with_paragraph( 10 | "Let us enjoy reading this Hindu Mythological Story \ 11 | of Solar Deities.", 12 | ) 13 | .with_paragraph( 14 | "Ravana once went to challenge Surya, the Sun-God, to a fight. \ 15 | When he reached the Solar Region he saw that the sun was about to rise and sent an envoy \ 16 | to inform Surya of his arrival and the reason for his coming.", 17 | ) 18 | .finish(); 19 | 20 | println!("{}", html_report); 21 | 22 | let markdown_report = MarkdownReportBuilder::new() 23 | .with_header("Why Snakes Have Forked Tongues") 24 | .with_paragraph( 25 | "Garuda brought the nectar after overcoming numerous obstacles, \ 26 | battling even the gods in the process.", 27 | ) 28 | .with_paragraph( 29 | "The nagas were delighted when he placed the pot \ 30 | containing the nectar before them. \ 31 | They let Vinata go and then they went to wash themselves before partaking of the ambrosia.", 32 | ) 33 | .with_header("Second header") 34 | .with_paragraph("Third paragraph") 35 | .finish(); 36 | 37 | println!("{}", markdown_report); 38 | } 39 | -------------------------------------------------------------------------------- /src/creational/singleton/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Singleton 2 | //! 3 | //! ## Type 4 | //! 5 | //! Creational 6 | //! 7 | //! ## Description 8 | //! 9 | //! Ensure a struct only has one instance and provide a global point of access to it. 10 | 11 | use std::mem; 12 | use std::sync::{Arc, Mutex, Once}; 13 | 14 | #[derive(Clone)] 15 | pub struct SingletonReader { 16 | // Since we will be used in many threads, we need to protect concurrent access 17 | pub inner: Arc>, 18 | } 19 | 20 | pub fn singleton() -> SingletonReader { 21 | // Initialize it to a null value 22 | static mut SINGLETON: *const SingletonReader = 0 as *const SingletonReader; 23 | static ONCE: Once = Once::new(); 24 | 25 | unsafe { 26 | ONCE.call_once(|| { 27 | // Make it 28 | let singleton = SingletonReader { 29 | inner: Arc::new(Mutex::new(World::new(0, 0))), 30 | }; 31 | 32 | // Put it in the heap so it can outlive this call 33 | SINGLETON = mem::transmute(Box::new(singleton)); 34 | }); 35 | 36 | // Now we give out a copy of the data that is safe to use concurrently. 37 | (*SINGLETON).clone() 38 | } 39 | } 40 | 41 | #[derive(Debug)] 42 | pub struct World { 43 | area: u64, 44 | population: u64, 45 | } 46 | 47 | impl World { 48 | pub fn new(area: u64, population: u64) -> Self { 49 | Self { area, population } 50 | } 51 | 52 | pub fn set_area(&mut self, area: u64) { 53 | self.area = area; 54 | } 55 | 56 | pub fn set_population(&mut self, population: u64) { 57 | self.population = population; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/structural/composite/roman_army/primitives.rs: -------------------------------------------------------------------------------- 1 | use super::ComponentUnit; 2 | 3 | pub struct Archer; 4 | pub struct Infantryman; 5 | pub struct Horseman; 6 | 7 | impl ComponentUnit for Archer { 8 | fn get_strength(&self) -> i32 { 9 | 1 10 | } 11 | 12 | fn add_unit(&mut self, _: Box) { 13 | unimplemented!() 14 | } 15 | 16 | fn get_units(&self) -> &Vec> { 17 | unimplemented!() 18 | } 19 | 20 | fn get_unit(&self, _: usize) -> &dyn ComponentUnit { 21 | unimplemented!() 22 | } 23 | 24 | fn remove(&mut self, _: usize) { 25 | unimplemented!() 26 | } 27 | } 28 | 29 | impl ComponentUnit for Infantryman { 30 | fn get_strength(&self) -> i32 { 31 | 2 32 | } 33 | 34 | fn add_unit(&mut self, _: Box) { 35 | unimplemented!() 36 | } 37 | 38 | fn get_units(&self) -> &Vec> { 39 | unimplemented!() 40 | } 41 | 42 | fn get_unit(&self, _: usize) -> &dyn ComponentUnit { 43 | unimplemented!() 44 | } 45 | 46 | fn remove(&mut self, _: usize) { 47 | unimplemented!() 48 | } 49 | } 50 | 51 | impl ComponentUnit for Horseman { 52 | fn get_strength(&self) -> i32 { 53 | 3 54 | } 55 | 56 | fn add_unit(&mut self, _: Box) { 57 | unimplemented!() 58 | } 59 | 60 | fn get_units(&self) -> &Vec> { 61 | unimplemented!() 62 | } 63 | 64 | fn get_unit(&self, _: usize) -> &dyn ComponentUnit { 65 | unimplemented!() 66 | } 67 | 68 | fn remove(&mut self, _: usize) { 69 | unimplemented!() 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.195.0/containers/rust 3 | { 4 | "name": "Rust Sample", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | "args": { 8 | // Use the VARIANT arg to pick a Debian OS version: buster, bullseye 9 | // Use bullseye when on local on arm64/Apple Silicon. 10 | "VARIANT": "buster" 11 | } 12 | }, 13 | "runArgs": [ 14 | "--cap-add=SYS_PTRACE", 15 | "--security-opt", 16 | "seccomp=unconfined" 17 | ], 18 | // Configure tool-specific properties. 19 | "customizations": { 20 | // Configure properties specific to VS Code. 21 | "vscode": { 22 | // Set *default* container specific settings.json values on container create. 23 | "settings": { 24 | "lldb.executable": "/usr/bin/lldb", 25 | // VS Code don't watch files under ./target 26 | "files.watcherExclude": { 27 | "**/target/**": true 28 | }, 29 | "rust-analyzer.checkOnSave.command": "clippy" 30 | }, 31 | // Add the IDs of extensions you want installed when the container is created. 32 | "extensions": [ 33 | "vadimcn.vscode-lldb", 34 | "mutantdino.resourcemonitor", 35 | "matklad.rust-analyzer", 36 | "tamasfe.even-better-toml", 37 | "serayuzgur.crates" 38 | ] 39 | } 40 | }, 41 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 42 | // "forwardPorts": [], 43 | // Use 'postCreateCommand' to run commands after the container is created. 44 | // "postCreateCommand": "rustc --version", 45 | // Comment out to run as root instead. 46 | "remoteUser": "vscode" 47 | } 48 | -------------------------------------------------------------------------------- /.github/workflows/rust-clippy.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # rust-clippy is a tool that runs a bunch of lints to catch common 6 | # mistakes in your Rust code and help improve your Rust code. 7 | # More details at https://github.com/rust-lang/rust-clippy 8 | # and https://rust-lang.github.io/rust-clippy/ 9 | 10 | name: rust-clippy analyze 11 | 12 | on: 13 | push: 14 | branches: [ master ] 15 | pull_request: 16 | # The branches below must be a subset of the branches above 17 | branches: [ master ] 18 | schedule: 19 | - cron: '21 5 * * 0' 20 | 21 | jobs: 22 | rust-clippy-analyze: 23 | name: Run rust-clippy analyzing 24 | runs-on: ubuntu-latest 25 | permissions: 26 | contents: read 27 | security-events: write 28 | steps: 29 | - name: Checkout code 30 | uses: actions/checkout@v2 31 | 32 | - name: Install Rust toolchain 33 | uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af #@v1 34 | with: 35 | profile: minimal 36 | toolchain: stable 37 | components: clippy 38 | override: true 39 | 40 | - name: Install required cargo 41 | run: cargo install clippy-sarif sarif-fmt 42 | 43 | - name: Run rust-clippy 44 | run: 45 | cargo clippy 46 | --all-features 47 | --message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt 48 | continue-on-error: true 49 | 50 | - name: Upload analysis results to GitHub 51 | uses: github/codeql-action/upload-sarif@v1 52 | with: 53 | sarif_file: rust-clippy-results.sarif 54 | wait-for-processing: true 55 | -------------------------------------------------------------------------------- /src/creational/builder/config.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Display}; 2 | 3 | pub struct Config { 4 | width: i32, 5 | height: i32, 6 | title: String, 7 | is_minimized: bool, 8 | } 9 | 10 | pub struct ConfigBuilder { 11 | width: i32, 12 | height: i32, 13 | title: String, 14 | is_minimized: bool, 15 | } 16 | 17 | impl ConfigBuilder { 18 | pub fn new() -> ConfigBuilder { 19 | ConfigBuilder { 20 | width: 100, 21 | height: 50, 22 | title: "Builder".to_string(), 23 | is_minimized: false, 24 | } 25 | } 26 | 27 | pub fn with_width(mut self, width: i32) -> ConfigBuilder { 28 | self.width = width; 29 | self 30 | } 31 | 32 | pub fn with_height(mut self, height: i32) -> ConfigBuilder { 33 | self.height = height; 34 | self 35 | } 36 | 37 | pub fn with_title + Display>(mut self, title: S) -> ConfigBuilder { 38 | self.title = title.into(); 39 | self 40 | } 41 | 42 | pub fn with_minimized(mut self, minimized: bool) -> ConfigBuilder { 43 | self.is_minimized = minimized; 44 | self 45 | } 46 | 47 | pub fn finalize(self) -> Config { 48 | Config { 49 | width: self.width, 50 | height: self.height, 51 | title: self.title, 52 | is_minimized: self.is_minimized, 53 | } 54 | } 55 | } 56 | 57 | impl Default for ConfigBuilder { 58 | fn default() -> Self { 59 | Self::new() 60 | } 61 | } 62 | 63 | impl fmt::Display for Config { 64 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 65 | write!( 66 | f, 67 | "Width: {}\nHeight: {}\nTitle: {}\nIs minimized: {}\n", 68 | self.width, self.height, self.title, self.is_minimized 69 | ) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/structural/facade/rock_band/black_sabbath.rs: -------------------------------------------------------------------------------- 1 | use super::musician::bassist::Bassist; 2 | use super::musician::drummer::Drummer; 3 | use super::musician::guitarist::Guitarist; 4 | use super::musician::vocalist::Vocalist; 5 | 6 | pub struct BlackSabbath { 7 | vocalist: Vocalist, 8 | guitarist: Guitarist, 9 | bassist: Bassist, 10 | drummer: Drummer, 11 | } 12 | 13 | impl BlackSabbath { 14 | pub fn new() -> BlackSabbath { 15 | BlackSabbath { 16 | vocalist: Vocalist::new("Ozzy Osbourne"), 17 | guitarist: Guitarist::new("Tony Iommi"), 18 | bassist: Bassist::new("Geezer Butler"), 19 | drummer: Drummer::new("Bill Ward"), 20 | } 21 | } 22 | 23 | pub fn play_cool_song(&self) { 24 | self.guitarist.play_cool_opening(); 25 | self.drummer.start_playing(); 26 | self.bassist.follow_the_drums(); 27 | self.guitarist.play_cool_riffs(); 28 | self.vocalist.sing_couplet(1); 29 | self.bassist.change_rhythm("chorus"); 30 | self.guitarist.play_another_cool_riffs(); 31 | self.vocalist.sing_chorus(); 32 | self.bassist.change_rhythm("verse"); 33 | self.guitarist.play_cool_riffs(); 34 | self.vocalist.sing_couplet(2); 35 | self.bassist.change_rhythm("chorus"); 36 | self.guitarist.play_another_cool_riffs(); 37 | self.vocalist.sing_chorus(); 38 | self.bassist.change_rhythm("verse"); 39 | self.guitarist.play_incredibly_cool_solo(); 40 | self.guitarist.play_cool_riffs(); 41 | self.vocalist.sing_couplet(3); 42 | self.bassist.change_rhythm("chorus"); 43 | self.guitarist.play_another_cool_riffs(); 44 | self.vocalist.sing_chorus(); 45 | self.bassist.change_rhythm("verse"); 46 | self.guitarist.play_cool_riffs(); 47 | self.bassist.stop_playing(); 48 | self.drummer.stop_playing(); 49 | self.guitarist.play_final_accord(); 50 | } 51 | } 52 | 53 | impl Default for BlackSabbath { 54 | fn default() -> Self { 55 | Self::new() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/behavioral/command/cohort.rs: -------------------------------------------------------------------------------- 1 | pub struct Cohort {} 2 | 3 | impl Cohort { 4 | fn move_to(&self, direction: String) { 5 | println!("The cohort moves to {}", direction); 6 | } 7 | 8 | fn stop(&self) { 9 | println!("The cohort stops"); 10 | } 11 | } 12 | 13 | trait Command { 14 | fn execute(&self, cohort: &Cohort); 15 | fn unexecute(&self, cohort: &Cohort); 16 | } 17 | 18 | pub struct AttackCommand {} 19 | 20 | impl Command for AttackCommand { 21 | fn execute(&self, cohort: &Cohort) { 22 | cohort.move_to("forward".to_string()); 23 | } 24 | 25 | fn unexecute(&self, cohort: &Cohort) { 26 | cohort.stop(); 27 | } 28 | } 29 | 30 | pub struct RetreatCommand {} 31 | 32 | impl Command for RetreatCommand { 33 | fn execute(&self, cohort: &Cohort) { 34 | cohort.move_to("back".to_string()); 35 | } 36 | 37 | fn unexecute(&self, cohort: &Cohort) { 38 | cohort.stop(); 39 | } 40 | } 41 | 42 | pub enum CurrentCommand { 43 | Attack, 44 | Retreat, 45 | } 46 | 47 | pub struct CohortCommander { 48 | cohort: Cohort, 49 | attack_command: AttackCommand, 50 | retreat_command: RetreatCommand, 51 | current_command: Option, 52 | } 53 | 54 | impl CohortCommander { 55 | pub fn new( 56 | cohort: Cohort, 57 | attack_command: AttackCommand, 58 | retreat_command: RetreatCommand, 59 | ) -> CohortCommander { 60 | CohortCommander { 61 | cohort, 62 | attack_command, 63 | retreat_command, 64 | current_command: None, 65 | } 66 | } 67 | 68 | pub fn attack(&mut self) { 69 | self.current_command = Some(CurrentCommand::Attack); 70 | self.attack_command.execute(&self.cohort); 71 | } 72 | 73 | pub fn retreat(&mut self) { 74 | self.current_command = Some(CurrentCommand::Retreat); 75 | self.retreat_command.execute(&self.cohort); 76 | } 77 | 78 | pub fn stop(&self) { 79 | match self.current_command { 80 | Some(CurrentCommand::Attack) => self.attack_command.unexecute(&self.cohort), 81 | Some(CurrentCommand::Retreat) => self.retreat_command.unexecute(&self.cohort), 82 | None => println!("The cohort is already stoping"), 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/structural/bridge/transportation.rs: -------------------------------------------------------------------------------- 1 | pub mod transport { 2 | use super::delivery::Delivery; 3 | 4 | pub trait Transport { 5 | fn load(&self); 6 | fn set_delivery(&mut self, delivery: Box); 7 | fn carry(&self); 8 | fn unload(&self); 9 | } 10 | 11 | pub struct Plane { 12 | delivery: Box, 13 | } 14 | 15 | impl Plane { 16 | pub fn new(delivery: Box) -> Plane { 17 | Plane { delivery } 18 | } 19 | } 20 | 21 | impl Transport for Plane { 22 | fn load(&self) { 23 | println!("Loading on the plane... Done!"); 24 | } 25 | 26 | fn set_delivery(&mut self, delivery: Box) { 27 | self.delivery = delivery; 28 | } 29 | 30 | fn carry(&self) { 31 | self.delivery.issue(); 32 | println!("The plane flew off... Done!"); 33 | } 34 | 35 | fn unload(&self) { 36 | println!("Unloading the plane... Done!\n"); 37 | } 38 | } 39 | 40 | pub struct Train { 41 | delivery: Box, 42 | } 43 | 44 | impl Train { 45 | pub fn new(delivery: Box) -> Train { 46 | Train { delivery } 47 | } 48 | } 49 | 50 | impl Transport for Train { 51 | fn load(&self) { 52 | println!("Loading on the train... Done!"); 53 | } 54 | 55 | fn set_delivery(&mut self, delivery: Box) { 56 | self.delivery = delivery; 57 | } 58 | 59 | fn carry(&self) { 60 | self.delivery.issue(); 61 | println!("The train left... Done!"); 62 | } 63 | 64 | fn unload(&self) { 65 | println!("Unloading the train... Done!\n"); 66 | } 67 | } 68 | } 69 | 70 | pub mod delivery { 71 | pub trait Delivery { 72 | fn issue(&self); 73 | } 74 | 75 | pub struct Express {} 76 | 77 | impl Delivery for Express { 78 | fn issue(&self) { 79 | println!("Super fast delivery for 3 days") 80 | } 81 | } 82 | 83 | pub struct Normal {} 84 | 85 | impl Delivery for Normal { 86 | fn issue(&self) { 87 | println!("Normal delivery for two weeks") 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/behavioral/interpreter/shop_assistant.rs: -------------------------------------------------------------------------------- 1 | use regex::Regex; 2 | use std::error::Error; 3 | 4 | #[derive(Debug, PartialEq)] 5 | pub struct Order { 6 | pub qty: u32, 7 | pub product: String, 8 | pub source: String, 9 | } 10 | 11 | impl Order { 12 | pub fn new>(qty: u32, product: S, source: S) -> Self { 13 | Self { 14 | qty, 15 | product: product.into(), 16 | source: source.into(), 17 | } 18 | } 19 | 20 | pub fn parse>(command: S) -> Result> { 21 | // Grammar Representationconst 22 | let optional_space = " ?"; 23 | let qty = String::from("x(?P\\d+)") + optional_space; 24 | let product = String::from("'(?P[\\w ]+)'") + optional_space; 25 | let source = "from (?P\\w+)"; 26 | let order_command = 27 | String::from("order") + optional_space + qty.as_str() + product.as_str() + source; 28 | 29 | let re = Regex::new(&order_command)?; 30 | let command = command.into(); 31 | let x = re.captures(command.as_ref()).ok_or("Bad command")?; 32 | 33 | Ok(Self { 34 | product: x 35 | .name("product") 36 | .ok_or("Product not defined")? 37 | .as_str() 38 | .to_string(), 39 | qty: x.name("qty").ok_or("Qty not defined")?.as_str().parse()?, 40 | source: x 41 | .name("source") 42 | .ok_or("Source not defined")? 43 | .as_str() 44 | .to_string(), 45 | }) 46 | } 47 | } 48 | 49 | impl Default for Order { 50 | fn default() -> Self { 51 | Self::new(1, "Ice cream", "Five") 52 | } 53 | } 54 | 55 | #[cfg(test)] 56 | mod tests { 57 | use super::Order; 58 | 59 | #[test] 60 | fn test_1() { 61 | let order = Order::new(12, "1L milk packs", "Macro"); 62 | let command = "order x12 '1L milk packs' from Macro"; 63 | assert_eq!(order, Order::parse(command).unwrap()); 64 | } 65 | 66 | #[test] 67 | fn test_2() { 68 | let order = Order::new(1, "a bag of potatoes", "Tesco"); 69 | let command = "order x1 'a bag of potatoes' from Tesco"; 70 | assert_eq!(order, Order::parse(command).unwrap()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/behavioral/chain_of_responsibility/police_department.rs: -------------------------------------------------------------------------------- 1 | pub trait Policeman { 2 | fn set_next(&mut self, next: Box); 3 | fn investigate(&self, crime: u8); 4 | } 5 | 6 | pub struct Detective { 7 | deduction: u8, 8 | next: Option>, 9 | name: String, 10 | } 11 | 12 | impl Detective { 13 | pub fn new>(deduction: u8, name: S) -> Detective { 14 | Detective { 15 | deduction, 16 | next: None, 17 | name: name.into(), 18 | } 19 | } 20 | } 21 | 22 | impl Policeman for Detective { 23 | fn set_next(&mut self, next: Box) { 24 | self.next = Some(next); 25 | } 26 | 27 | fn investigate(&self, crime: u8) { 28 | if crime > self.deduction { 29 | println!( 30 | "Detective {}: I can't investigate it. I need help.", 31 | self.name 32 | ); 33 | 34 | match self.next { 35 | Some(ref next) => next.investigate(crime), 36 | None => println!("Detective {}: Unimpossible for our department", self.name), 37 | } 38 | } else { 39 | println!("Detective {}: I can do this.", self.name); 40 | } 41 | } 42 | } 43 | 44 | pub struct Patrolman { 45 | deduction: u8, 46 | next: Option>, 47 | name: String, 48 | } 49 | 50 | impl Patrolman { 51 | pub fn new>(deduction: u8, name: S) -> Patrolman { 52 | Patrolman { 53 | deduction, 54 | next: None, 55 | name: name.into(), 56 | } 57 | } 58 | } 59 | 60 | impl Policeman for Patrolman { 61 | fn set_next(&mut self, next: Box) { 62 | self.next = Some(next); 63 | } 64 | 65 | fn investigate(&self, crime: u8) { 66 | if crime > self.deduction { 67 | println!( 68 | "Patrolman {}: I'm just a patrolman. I need help.", 69 | self.name 70 | ); 71 | 72 | match self.next { 73 | Some(ref next) => next.investigate(crime), 74 | None => println!("Patrolman {}: Unimpossible for our department.", self.name), 75 | } 76 | } else { 77 | println!("Patrolman {}: It's easy. I can do this.", self.name); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/creational/abstract_factory/launch/mod.rs: -------------------------------------------------------------------------------- 1 | mod drink; 2 | mod launch_factory; 3 | mod main_course; 4 | 5 | pub use drink::{BusinessDrink, CheapDrink, Drink}; 6 | pub use launch_factory::{BusinessLaunchFactory, CheapLaunchFactory, LaunchFactory}; 7 | pub use main_course::{BusinessCourse, CheapCourse, MainCourse}; 8 | 9 | pub trait Launch 10 | where 11 | M: MainCourse, 12 | D: Drink, 13 | { 14 | fn get_main_course(&self) -> M; 15 | fn get_drink(&self) -> D; 16 | 17 | fn print_prices(&self, launch: &L) 18 | where 19 | M: MainCourse, 20 | D: Drink, 21 | L: Launch, 22 | { 23 | let maine_course = launch.get_main_course(); 24 | let drink = launch.get_drink(); 25 | 26 | self.print_main_course_price(&maine_course); 27 | self.print_drink_price(&drink); 28 | } 29 | 30 | fn print_main_course_price(&self, main_course: &M) { 31 | println!("Main Course: {}$", main_course.get_price()); 32 | } 33 | 34 | fn print_drink_price(&self, drink: &D) { 35 | println!("Drink: {}$\n", drink.get_price()); 36 | } 37 | } 38 | 39 | pub struct CheapLaunch { 40 | pub main_course: CheapCourse, 41 | pub drink: CheapDrink, 42 | } 43 | 44 | impl CheapLaunch { 45 | pub fn new() -> CheapLaunch { 46 | CheapLaunch { 47 | main_course: CheapCourse::new(110), 48 | drink: CheapDrink::new(50), 49 | } 50 | } 51 | } 52 | 53 | impl Default for CheapLaunch { 54 | fn default() -> Self { 55 | Self::new() 56 | } 57 | } 58 | 59 | impl Launch for CheapLaunch { 60 | fn get_main_course(&self) -> CheapCourse { 61 | self.main_course 62 | } 63 | 64 | fn get_drink(&self) -> CheapDrink { 65 | self.drink 66 | } 67 | } 68 | 69 | pub struct BusinessLaunch { 70 | pub main_course: BusinessCourse, 71 | pub drink: BusinessDrink, 72 | } 73 | 74 | impl BusinessLaunch { 75 | pub fn new() -> BusinessLaunch { 76 | BusinessLaunch { 77 | main_course: BusinessCourse::new(1_500), 78 | drink: BusinessDrink::new(250), 79 | } 80 | } 81 | } 82 | 83 | impl Default for BusinessLaunch { 84 | fn default() -> Self { 85 | Self::new() 86 | } 87 | } 88 | 89 | impl Launch for BusinessLaunch { 90 | fn get_main_course(&self) -> BusinessCourse { 91 | self.main_course 92 | } 93 | 94 | fn get_drink(&self) -> BusinessDrink { 95 | self.drink 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/creational/builder/report.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Display}; 2 | 3 | pub struct Report { 4 | pub content: String, 5 | pub items: i32, 6 | pub format: String, 7 | } 8 | 9 | impl fmt::Display for Report { 10 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 11 | write!( 12 | f, 13 | "{}\nItems: {}\nFormat: {}", 14 | self.content, self.items, self.format 15 | ) 16 | } 17 | } 18 | 19 | pub trait ReportBuilder> { 20 | fn with_header + Display>(self, header: S) -> T; 21 | fn with_paragraph + Display>(self, paragraph: S) -> T; 22 | fn finish(self) -> Report; 23 | } 24 | 25 | pub struct HtmlReportBuilder { 26 | content: String, 27 | items: i32, 28 | } 29 | 30 | impl HtmlReportBuilder { 31 | pub fn new() -> HtmlReportBuilder { 32 | HtmlReportBuilder { 33 | content: "".to_string(), 34 | items: 0, 35 | } 36 | } 37 | } 38 | 39 | impl Default for HtmlReportBuilder { 40 | fn default() -> Self { 41 | Self::new() 42 | } 43 | } 44 | 45 | impl ReportBuilder for HtmlReportBuilder { 46 | fn with_header + Display>(mut self, header: S) -> HtmlReportBuilder { 47 | self.content.push_str(&format!("

{}

\n", header)); 48 | self.items += 1; 49 | self 50 | } 51 | 52 | fn with_paragraph + Display>(mut self, paragraph: S) -> HtmlReportBuilder { 53 | self.content.push_str(&format!("

{}

\n", paragraph)); 54 | self.items += 1; 55 | self 56 | } 57 | 58 | fn finish(self) -> Report { 59 | Report { 60 | content: self.content, 61 | items: self.items, 62 | format: "Html\n".to_string(), 63 | } 64 | } 65 | } 66 | 67 | pub struct MarkdownReportBuilder { 68 | content: String, 69 | items: i32, 70 | } 71 | 72 | impl MarkdownReportBuilder { 73 | pub fn new() -> MarkdownReportBuilder { 74 | MarkdownReportBuilder { 75 | content: "".to_string(), 76 | items: 0, 77 | } 78 | } 79 | } 80 | 81 | impl Default for MarkdownReportBuilder { 82 | fn default() -> Self { 83 | Self::new() 84 | } 85 | } 86 | 87 | impl ReportBuilder for MarkdownReportBuilder { 88 | fn with_header + Display>(mut self, header: S) -> MarkdownReportBuilder { 89 | self.content.push_str(&format!("# {}\n\n", header)); 90 | self.items += 1; 91 | self 92 | } 93 | 94 | fn with_paragraph + Display>(mut self, paragraph: S) -> MarkdownReportBuilder { 95 | self.content.push_str(&format!("{}\n\n", paragraph)); 96 | self.items += 1; 97 | self 98 | } 99 | 100 | fn finish(self) -> Report { 101 | Report { 102 | content: self.content, 103 | items: self.items, 104 | format: "Markdown\n".to_string(), 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Design patterns via Rust 2 | 3 | ## Types of Patterns 4 | 5 | 1. Behavioral ([Wiki](https://en.wikipedia.org/wiki/Behavioral_pattern)) 6 | 1. Creational ([Wiki](https://en.wikipedia.org/wiki/Creational_pattern)) 7 | 1. Structural ([Wiki](https://en.wikipedia.org/wiki/Structural_pattern)) 8 | 9 | ## Template List 10 | 11 | Template name | Type | Status | Links | 12 | :-----------------------|:----------:|:------:|:----------------------------------------------------------------------| 13 | Abstract Factory | Creational | ✅ | [Wiki](https://en.wikipedia.org/wiki/Abstract_factory_pattern) | 14 | Adapter | Structural | ✅ | [Wiki](https://en.wikipedia.org/wiki/Adapter_pattern) | 15 | Bridge | Structural | ✅ | [Wiki](https://en.wikipedia.org/wiki/Bridge_pattern) | 16 | Builder | Creational | ✅ | [Wiki](https://en.wikipedia.org/wiki/Builder_pattern) | 17 | Chain of Responsibility | Behavioral | ✅ | [Wiki](https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern) | 18 | Command | Behavioral | ✅ | [Wiki](https://en.wikipedia.org/wiki/Command_pattern) | 19 | Composite | Structural | ✅ | [Wiki](https://en.wikipedia.org/wiki/Composite_pattern) | 20 | Decorator | Structural | ✅ | [Wiki](https://en.wikipedia.org/wiki/Decorator_pattern) | 21 | Facade | Structural | ✅ | [Wiki](https://en.wikipedia.org/wiki/Facade_pattern) | 22 | Factory Method | Creational | ✅ | [Wiki](https://en.wikipedia.org/wiki/Factory_method_pattern) | 23 | Flyweight | Structural | ✅ | [Wiki](https://en.wikipedia.org/wiki/Flyweight_pattern) | 24 | Interpreter | Behavioral | ✅ | [Wiki](https://en.wikipedia.org/wiki/Interpreter_pattern) | 25 | Iterator | Behavioral | ⏳ | [Wiki](https://en.wikipedia.org/wiki/Iterator_pattern) | 26 | Mediator | Behavioral | ⏳ | [Wiki](https://en.wikipedia.org/wiki/Mediator_pattern) | 27 | Memento | Behavioral | ⏳ | [Wiki](https://en.wikipedia.org/wiki/Memento_pattern) | 28 | Prototype | Creational | ✅ | [Wiki](https://en.wikipedia.org/wiki/Prototype_pattern) | 29 | Proxy | Structural | ✅ | [Wiki](https://en.wikipedia.org/wiki/Proxy_pattern) | 30 | Observer | Behavioral | ⏳ | [Wiki](https://en.wikipedia.org/wiki/Observer_pattern) | 31 | Singleton | Creational | ✅ | [Wiki](https://en.wikipedia.org/wiki/Singleton_pattern) | 32 | State | Behavioral | ⏳ | [Wiki](https://en.wikipedia.org/wiki/State_pattern) | 33 | Strategy | Behavioral | ⏳ | [Wiki](https://en.wikipedia.org/wiki/Strategy_pattern) | 34 | Template Method | Behavioral | ⏳ | [Wiki](https://en.wikipedia.org/wiki/Template_method_pattern) | 35 | Visitor | Behavioral | ⏳ | [Wiki](https://en.wikipedia.org/wiki/Visitor_pattern) | 36 | 37 | ## Useful links 38 | 39 | * [Rust Design Patterns](https://rust-unofficial.github.io/patterns/intro.html) 40 | * [Design Patterns on Wiki](https://en.wikipedia.org/wiki/Design_Patterns) 41 | * [Design patterns card](http://www.mcdonaldland.info/files/designpatterns/designpatternscard.pdf) 42 | * [Шпаргалка по шаблонам проектирования](https://habrahabr.ru/post/210288/) 43 | --------------------------------------------------------------------------------