Factory Method Design Pattern in Rust

1. Definition

The Factory Method is a creational design pattern that provides an interface for creating objects in a super class, but allows subclasses to alter the type of objects that will be created. Instead of directly calling the constructor of an object, a factory method is used to produce the object, allowing for greater flexibility and separation of responsibilities.

2. Problem Statement

Imagine you're building a UI toolkit where each OS (Windows, macOS, Linux) has different buttons. Writing conditional code every time you need to create a button for a specific OS makes the codebase complicated and hard to maintain.

3. Solution

With the Factory Method, you define an interface for creating a button but allow the subclasses (specific OS implementations) to decide which class to instantiate. This way, you abstract away the creation logic from the main application.

4. Real-World Use Cases

1. GUI libraries where each OS provides a different implementation of a widget like buttons or windows.

2. Database connectors where different databases need different initialization processes.

5. Implementation Steps

1. Define a creator interface with the factory method.

2. Concrete classes will implement this interface to produce products.

3. Define a product interface.

4. Concrete products implement this product interface.

5. Use the factory method to create and return products.

6. Implementation in Rust Programming

// Step 1 & 3: Product interface and its concrete implementation
trait Button {
    fn render(&self);
}
struct WindowsButton;
impl Button for WindowsButton {
    fn render(&self) {
        println!("Rendering a Windows button");
    }
}
struct MacButton;
impl Button for MacButton {
    fn render(&self) {
        println!("Rendering a Mac button");
    }
}
// Step 2: Creator interface and its concrete implementations
trait ButtonFactory {
    fn create_button(&self) -> Box<dyn Button>;
}
struct WindowsButtonFactory;
impl ButtonFactory for WindowsButtonFactory {
    fn create_button(&self) -> Box<dyn Button> {
        Box::new(WindowsButton)
    }
}
struct MacButtonFactory;
impl ButtonFactory for MacButtonFactory {
    fn create_button(&self) -> Box<dyn Button> {
        Box::new(MacButton)
    }
}
// Step 5: Using the factory method
fn main() {
    let factory: Box<dyn ButtonFactory> = if cfg!(target_os = "windows") {
        Box::new(WindowsButtonFactory)
    } else {
        Box::new(MacButtonFactory)
    };
    let button = factory.create_button();
    button.render();
}

Output:

Depending on the target OS:
"Rendering a Windows button" or "Rendering a Mac button"

Explanation:

1. The Button trait represents the abstract product, while WindowsButton and MacButton are concrete products.

2. The ButtonFactory trait represents the creator interface, and its concrete implementations decide which button to produce.

3. In the main function, depending on the target OS, an appropriate factory is chosen and a button is created using the factory method.

7. When to use?

The Factory Method pattern is especially useful when:

1. The exact type of the object to be created isn't known until runtime.

2. You want to delegate the responsibility of object creation to subclasses.

3. You want to localize the knowledge of which class gets instantiated.


Comments