In the last article I explored how to implement the Singleton pattern in Rust, I will continue with another creational design pattern, the Abstract Factory pattern.
The Abstract Factory pattern is a creational design pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes. This pattern is particularly useful when the system needs to be independent of the way its objects are created. Let’s explore the Abstract Factory pattern in Rust with a practical example involving themed buttons and checkboxes.
The code can be found in Github
First, we define the traits for our products. In this example, we’ll have Button
and Checkbox
as our products, each with a render method. The Checkbox
trait also includes a default toggle_check
method.
// Define the Button trait with a render method
trait Button {
fn render(&self);
}
// Define the Checkbox trait with a render method and a default toggle_check method
trait Checkbox {
fn render(&self);
fn toggle_check(&self) {
println!("Toggled")
}
}
Next, we create concrete implementations for these products for both Dark and Light themes.
// Define concrete implementations for Dark and Light themed buttons and checkboxes
struct DarkButton;
struct DarkCheckbox;
struct LightButton;
struct LightCheckbox;
// Implement the Button trait for DarkButton
impl Button for DarkButton {
fn render(&self) {
println!("I am a dark button")
}
}
// Implement the Button trait for LightButton
impl Button for LightButton {
fn render(&self) {
println!("I am a light button")
}
}
// Implement the Checkbox trait for DarkCheckbox
impl Checkbox for DarkCheckbox {
fn render(&self) {
println!("I am a dark checkbox")
}
}
// Implement the Checkbox trait for LightCheckbox
impl Checkbox for LightCheckbox {
fn render(&self) {
println!("I am a light checkbox")
}
}
We define an abstract factory trait that declares methods for creating abstract products. This trait will be implemented by our concrete factories.
// Define the ThemeFactory trait with methods to create buttons and checkboxes
trait ThemeFactory {
fn create_button(&self) -> Box<dyn Button>;
fn create_checkbox(&self) -> Box<dyn Checkbox>;
}
We then implement the concrete factories for creating Dark and Light theme products.
// Define concrete factories for Dark and Light themes
struct DarkFactory;
struct LightFactory;
// Implement the ThemeFactory trait for DarkFactory
impl ThemeFactory for DarkFactory {
fn create_button(&self) -> Box<dyn Button> {
Box::new(DarkButton)
}
fn create_checkbox(&self) -> Box<dyn Checkbox> {
Box::new(DarkCheckbox)
}
}
// Implement the ThemeFactory trait for LightFactory
impl ThemeFactory for LightFactory {
fn create_button(&self) -> Box<dyn Button> {
Box::new(LightButton)
}
fn create_checkbox(&self) -> Box<dyn Checkbox> {
Box::new(LightCheckbox)
}
}
Finally, we write a function to render the UI using a given theme factory. This function will create and render the themed buttons and checkboxes.
// Function to render the UI using a given theme factory
fn render_ui(factory: &dyn ThemeFactory) {
let button: Box<dyn Button> = factory.create_button();
let checkbox: Box<dyn Checkbox> = factory.create_checkbox();
button.render();
checkbox.render();
}
fn main() {
let dark_factory: Box<dyn ThemeFactory> = Box::new(DarkFactory);
let light_factory: Box<dyn ThemeFactory> = Box::new(LightFactory);
println!("Rendering Dark Theme:");
render_ui(&*dark_factory);
println!("\nRendering Light Theme:");
render_ui(&*light_factory);
}
The Abstract Factory pattern is a powerful tool for creating families of related objects while keeping the client code decoupled from their specific classes (or structs in the case of Rust).