Creational Patterns
Recipe | Crates | Categories |
---|---|---|
Implement a Factory | ||
Implement an Abstract Factory | ||
Implement a Singleton |
FIXME
Implement a Factory
//! A factory is quite useful when you need to create objects of different types //! that share a common interface, without needing to know the exact type at //! compile time. // Define a common trait for all shapes. // This defines the common interface that all concrete shape types will // implement. trait Shape { fn draw(&self); } // A concrete implementation of the Shape trait. struct Circle { radius: f64, } impl Shape for Circle { fn draw(&self) { println!("Drawing a circle with radius: {}", self.radius); } } // Another concrete implementation. struct Square { side: f64, } impl Shape for Square { fn draw(&self) { println!("Drawing a square with side: {}", self.side); } } // A enum that represents the types of shapes we can create. enum ShapeType { Circle, Square, } // The Factory. struct ShapeFactory; impl ShapeFactory { // The logic for creating different shape objects is centralized. fn create_shape(shape_type: ShapeType, size: f64) -> Box<dyn Shape> { match shape_type { ShapeType::Circle => Box::new(Circle { radius: size }), ShapeType::Square => Box::new(Square { side: size }), } } } fn main() { // Using the factory to create different shapes. let circle = ShapeFactory::create_shape(ShapeType::Circle, 5.0); let square = ShapeFactory::create_shape(ShapeType::Square, 10.0); // We use the ShapeFactory to create instances of Circle and Square without // directly knowing their concrete types. We only interact with them // through the Shape trait. circle.draw(); square.draw(); }
Implement an Abstract Factory
//! The Abstract Factory pattern creates families of related objects without //! tying-in to their concrete implementations. //! //! - The client code (in `main()`) doesn't need to know the specific classes of //! the objects it creates. //! - The pattern ensures that the objects created belong to the same family, //! enforcing consistency. //! //! The following example simulates building a UI library that needs to support //! different themes, like "Light" and "Dark." Each theme will have its own set //! of widgets: buttons, text boxes, and so on. The Abstract Factory pattern //! lets you define an interface for creating these families of widgets, //! and then you can have concrete factories for each theme. // Operations common to all concrete objects. trait Button { fn render(&self); } trait TextBox { fn display_text(&self, text: &str); } // Abstract factory interface (trait). trait GUIFactory { fn create_button(&self) -> Box<dyn Button>; fn create_text_box(&self) -> Box<dyn TextBox>; } mod light { use super::*; // Concrete object implementations for Light Theme. struct LightButton; impl Button for LightButton { fn render(&self) { println!("Rendering a light button."); } } struct LightTextBox; impl TextBox for LightTextBox { fn display_text(&self, text: &str) { println!("Displaying '{}' in a light text box.", text); } } // The concrete factory implementation produce concrete objects belonging to // a specific family (here, light theme). pub struct LightGUIFactory; impl GUIFactory for LightGUIFactory { fn create_button(&self) -> Box<dyn Button> { Box::new(LightButton) } fn create_text_box(&self) -> Box<dyn TextBox> { Box::new(LightTextBox) } } } mod dark { use super::*; // Concrete object implementations for Dark Theme. struct DarkButton; impl Button for DarkButton { fn render(&self) { println!("Rendering a dark button."); } } struct DarkTextBox; impl TextBox for DarkTextBox { fn display_text(&self, text: &str) { println!("Displaying '{}' in a dark text box.", text); } } // Concrete Factory for Dark Theme. pub struct DarkGUIFactory; impl GUIFactory for DarkGUIFactory { fn create_button(&self) -> Box<dyn Button> { Box::new(DarkButton) } fn create_text_box(&self) -> Box<dyn TextBox> { Box::new(DarkTextBox) } } } fn render(factory: &dyn GUIFactory, txt: &str) { let button = factory.create_button(); let text_box = factory.create_text_box(); button.render(); text_box.display_text(txt); } fn main() { // Using the Light theme factory. render(&light::LightGUIFactory, "Hello from light theme!"); println!(); // Using the Dark theme factory. render(&dark::DarkGUIFactory, "Hello from dark theme!"); }
See also:
Implement a Singleton
use std::sync::Mutex; use std::sync::OnceLock; struct Settings { config_value: String, } impl Settings { fn new(value: String) -> Self { Settings { config_value: value, } } fn get_config(&self) -> &str { &self.config_value } fn set_config(&mut self, new_value: String) { self.config_value = new_value; } } // OnceCell is a thread-safe cell that can be written to only once. // We wrap our Settings instance in a Mutex to ensure thread-safe access and // modification. Since multiple threads might try to access the singleton, we // need to protect it from race conditions. static SETTINGS: OnceLock<Mutex<Settings>> = OnceLock::new(); // You could also use `std::sync::LazyLock`. // Single point of access to our Settings instance. fn get_settings() -> &'static Mutex<Settings> { // If SETTINGS hasn't been initialized yet, execute the closure. SETTINGS .get_or_init(|| Mutex::new(Settings::new("default_config".to_string()))) } fn main() { // `OnceLock` has not been written to yet. assert!(SETTINGS.get().is_none()); // Acquire a lock. let settings_guard_1 = get_settings().lock().unwrap(); println!("Config 1: {}", settings_guard_1.get_config()); drop(settings_guard_1); // Release the lock. { let mut settings_guard_2 = get_settings().lock().unwrap(); // The MutexGuard provides mutable access. settings_guard_2.set_config("new_config".to_string()); println!("Config 2 updated!"); } // Lock released here. let settings_guard_3 = get_settings().lock().unwrap(); println!("Config 3: {}", settings_guard_3.get_config()); }