Skip to content
Author: ytianle

Factory Method⚓︎

"I know what 'type' I need, but which specific subclass to new is left to the subclass to decide."

picture 0

Characteristics⚓︎

  • Define an interface for creating an object.
  • Let subclasses decide which class to instantiate.
  • Defer instantiation to subclasses.
Factory Method vs Abstract Factory
  • Factory Method: each concrete factory creates one concrete product (e.g., CircleFactory → Circle, RectangleFactory → Rectangle).
  • `Abstract Factory: used to create families of related products together (e.g., a UI theme producing matching Button and Checkbox variants).

e.g: Boba shop⚓︎

Boba shop makes milk tea. Shanghai Boba shop only makes Taro Milk Tea. Beijing Boba shop only makes Matcha Milk Tea.

Hands dirty - "Enemy Spawner"⚓︎

We want to create different types of enemies (e.g., Goblin, Troll) in a game. The specific enemy type to spawn is determined by the game level.

image source

Meaning of ←

A dashed arrowhead line indicates a class that instantiates objects of another class. The arrow points to the class of the instantiated objects

We want the code deciding which enemy to spawn at runtime, in Main():

  • Get level from config
  • Choose concrete factory (GoblinFactory or TrollFactory) based on level
  • Inject factory into Game (Compose concrete factory in Game over Inheritance)
  • Factory creates specific Enemy

    using System;
    
    public interface IEnemy
    {
        void Attack();
    }
    public class Goblin : IEnemy
    {
        public void Attack() => Console.WriteLine("Goblin attacks with a club!");
    }
    public class Troll : IEnemy
    {
        public void Attack() => Console.WriteLine("Troll attacks with a hammer!");
    }
    
    public abstract class EnemyFactory
    {
        public abstract IEnemy CreateEnemy();
    }
    public class GoblinFactory : EnemyFactory
    {
        public override IEnemy CreateEnemy() => new Goblin();
    }
    public class TrollFactory : EnemyFactory
    {
        public override IEnemy CreateEnemy() => new Troll();
    }
    
    // Client code using dependency injection: pass a concrete factory to `Game`.
    class Game
    {
        private EnemyFactory factory;
        public Game(EnemyFactory factory) { this.factory = factory; }
        public void Run()
        {
            IEnemy enemy = factory.CreateEnemy();
            enemy.Attack();
        }
        static void Main()
        {
            // Compose application with a concrete factory
            string level = GetLevelFromConfig(); // e.g. "Forest", "Cave"
            EnemyFactory factory = level switch
            {
                "Forest" => new GoblinFactory(),
                "Cave"   => new TrollFactory(),
                _        => new GoblinFactory()
            };
            var game = new Game(factory);
            game.Run();
        }
    }
    
    #include <iostream>
    #include <memory>
    #include <string>
    
    class IEnemy {
    public:
        virtual void attack() const = 0;
        virtual ~IEnemy() = default;
    };
    class Goblin : public IEnemy {
    public:
        void attack() const override { std::cout << "Goblin attacks with a club!\n"; }
    };
    class Troll : public IEnemy {
    public:
        void attack() const override { std::cout << "Troll attacks with a hammer!\n"; }
    };
    
    class EnemyFactory {
    public:
        virtual std::unique_ptr<IEnemy> createEnemy() const = 0;
        virtual ~EnemyFactory() = default;
    };
    class GoblinFactory : public EnemyFactory {
    public:
        std::unique_ptr<IEnemy> createEnemy() const override { return std::make_unique<Goblin>(); }
    };
    class TrollFactory : public EnemyFactory {
    public:
        std::unique_ptr<IEnemy> createEnemy() const override { return std::make_unique<Troll>(); }
    };
    
    // Client code using dependency injection: provide a concrete factory to `Game`.
    class Game {
    public:
        explicit Game(std::unique_ptr<EnemyFactory> f) : factory(std::move(f)) {}
        void run() const {
            auto enemy = factory->createEnemy();
            enemy->attack();
        }
    private:
        std::unique_ptr<EnemyFactory> factory;
    };
    
    int main() {
        std::string level = getLevelFromConfig(); // "Forest" / "Cave"
        std::unique_ptr<EnemyFactory> factory;
        if (level == "Forest") factory = std::make_unique<GoblinFactory>();
        else if (level == "Cave") factory = std::make_unique<TrollFactory>();
        else factory = std::make_unique<GoblinFactory>();
    
        Game game(std::move(factory));
        game.run();
    }