Factory Pattern in Rust

https://en.wikipedia.org/wiki/Factory_method_pattern

Let’s look at another design pattern – “The factory pattern”

If you like these articles, there is a great site where you can learn more about Design Patterns : https://refactoring.guru/

The factory pattern is a design pattern in programming that helps create objects (or instances of classes) without telling the code exactly which kind of object to make.

Think of it like a factory in real life: a factory can make different types of products, but you don’t have to worry about how each product is made inside. You just say, “I need a product,” and the factory takes care of the details.

scrapeops

Here’s a simple example to make it clearer:

factory pattern in Rust

Factory Pattern

Imagine you’re in a video game where you can spawn different types of enemies: maybe a “Zombie,” a “Dragon,” or a “Robot.” You could have a bunch of code that manually creates each enemy, but that would get messy and hard to manage if you add more types later.

Instead, you can use a factory to do this for you. You just tell the factory, “Hey, I need an enemy,” and pass in what type of enemy you want. The factory takes care of creating the correct enemy without you needing to know how it works behind the scenes.

In simple terms:

  • Without a factory: You do all the work of making each different enemy.
  • With a factory: You let the factory do the work, so it’s easier to add new enemy types later.

This helps keep your code organized, flexible, and easier to maintain, especially when you want to add or change things later.

Let’s implement a factory pattern in Rust, using the example of spawning different types of enemies in a video game: Zombie, Dragon, and Robot.

Factory Pattern in Rust

Step 1: Define an Enemy Trait

We’ll create a common trait Enemy that all types of enemies will implement. The trait will have a attack method to define each enemy’s attack style.

trait Enemy {
    fn attack(&self) -> String;
}

Step 2: Define the Enemy Types

Next, we define three types of enemies: Zombie, Dragon, and Robot, each implementing the Enemy trait.

struct Zombie;

impl Enemy for Zombie {
    fn attack(&self) -> String {
        "Zombie attacks with a bite!".to_string()
    }
}

struct Dragon;

impl Enemy for Dragon {
    fn attack(&self) -> String {
        "Dragon breathes fire!".to_string()
    }
}

struct Robot;

impl Enemy for Robot {
    fn attack(&self) -> String {
        "Robot fires lasers!".to_string()
    }
}

Step 3: Create the Factory Function

Now, let’s create a factory function that will return the correct type of enemy based on the input.

enum EnemyType {
    Zombie,
    Dragon,
    Robot,
}

fn enemy_factory(enemy_type: EnemyType) -> Box<dyn Enemy> {
    match enemy_type {
        EnemyType::Zombie => Box::new(Zombie),
        EnemyType::Dragon => Box::new(Dragon),
        EnemyType::Robot => Box::new(Robot),
    }
}

Explanation:

  • Enum EnemyType: Used to decide which type of enemy to create.
  • enemy_factory function: Takes an EnemyType and returns a boxed Enemy object. In Rust, Box<dyn Enemy> allows us to store and return any type that implements the Enemy trait, making it possible to return different types from the factory.

Step 4: Using the Factory

Now we can use the factory to spawn different types of enemies and make them attack.

fn main() {
    let zombie = enemy_factory(EnemyType::Zombie);
    let dragon = enemy_factory(EnemyType::Dragon);
    let robot = enemy_factory(EnemyType::Robot);

    println!("{}", zombie.attack());
    println!("{}", dragon.attack());
    println!("{}", robot.attack());
}

Output:

Zombie attacks with a bite!
Dragon breathes fire!
Robot fires lasers!

Full Code:

trait Enemy {
    fn attack(&self) -> String;
}

struct Zombie;

impl Enemy for Zombie {
    fn attack(&self) -> String {
        "Zombie attacks with a bite!".to_string()
    }
}

struct Dragon;

impl Enemy for Dragon {
    fn attack(&self) -> String {
        "Dragon breathes fire!".to_string()
    }
}

struct Robot;

impl Enemy for Robot {
    fn attack(&self) -> String {
        "Robot fires lasers!".to_string()
    }
}

enum EnemyType {
    Zombie,
    Dragon,
    Robot,
}

fn enemy_factory(enemy_type: EnemyType) -> Box<dyn Enemy> {
    match enemy_type {
        EnemyType::Zombie => Box::new(Zombie),
        EnemyType::Dragon => Box::new(Dragon),
        EnemyType::Robot => Box::new(Robot),
    }
}

fn main() {
    let zombie = enemy_factory(EnemyType::Zombie);
    let dragon = enemy_factory(EnemyType::Dragon);
    let robot = enemy_factory(EnemyType::Robot);

    println!("{}", zombie.attack());
    println!("{}", dragon.attack());
    println!("{}", robot.attack());
}

Summary:

  • We use the factory pattern to abstract away the creation of different enemy types.
  • The enemy_factory function can easily be expanded to support new enemies by adding new cases to the EnemyType enum and implementing the Enemy trait for them.

Factory Pattern in Rust – example 2

#![allow(unused)]
trait Sounds {
    fn make_sound(&self);
}

enum Pet {
    Cat,
    Mouse,
}

struct Cat;
struct Mouse;

impl Sounds for Cat {
    fn make_sound(&self) {
        println!("{:?}", "miaow");
    }
}

impl Sounds for Mouse {
    fn make_sound(&self) {
        println!("{:?}", "eek");
    }
}

fn pet_factory(input: Pet) -> Box<dyn Sounds> {
    match input {
        Pet::Cat => Box::new(Cat),
        Pet::Mouse => Box::new(Mouse),
    }
}

fn main() {
    let pet1 = pet_factory(Pet::Cat);
    let pet2 = pet_factory(Pet::Mouse);
    pet1.make_sound();
    pet2.make_sound();
}

Previous article

On premise AI for business