Design Patterns: Abstract Factory

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.

factory

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

Defining the Products

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")
    }
}

Implementing Concrete Products

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")
    }
}

Creating the Abstract Factory Interface

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>;
}

Implementing Concrete Factories

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)
    }
}

Rendering the UI

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).

Published 8 Jun 2024

Tüftler (someone who enjoys working on and solving technical problems, often in a meticulous and innovative manner). Opinions are my own and not necessarily the views of my employer.
Avraam Mavridis on Twitter