Skip to content
Author: ytianle

Abstract Factory⚓︎

"I want a whole set of products that match each other."

picture 0

Characteristics⚓︎

  • families of related products
  • composition over inheritance
  • switch factory = switch entire product families

eg: windows ui / mac os ui⚓︎

  • windows factory -> windows button + windows checkbox
  • mac os factory -> mac os button + mac os checkbox

Hands dirty - "Level Theme factory"⚓︎

We want to create level themes in a game. Each theme has a matching set of Enemy and Obstacle.

  • ForestThemeFactory creates Goblin, forest monsters, forest weapons and Tree
  • CaveThemeFactory creates Troll, cave monsters, cave weapons and Cactus
  • IceThemeFactory creates Yeti, ice monsters, ice weapons and Iceberg

image source

Implementation
using System;

public interface IEnemy { string Name { get; } }
public interface IObstacle { string Name { get; } }
public interface IWeapon { string Name { get; } }

public interface ILevelThemeFactory
{
    IEnemy CreateEnemy();
    IEnemy CreateMinion();
    IObstacle CreateObstacle();
    IWeapon CreateWeapon();
}

// Forest
public class Goblin : IEnemy { public string Name => "Goblin"; }
public class ForestMonster : IEnemy { public string Name => "Forest Monster"; }
public class Tree : IObstacle { public string Name => "Tree"; }
public class ForestWeapon : IWeapon { public string Name => "Forest Weapon"; }

public class ForestThemeFactory : ILevelThemeFactory
{
    public IEnemy CreateEnemy() => new Goblin();
    public IEnemy CreateMinion() => new ForestMonster();
    public IObstacle CreateObstacle() => new Tree();
    public IWeapon CreateWeapon() => new ForestWeapon();
}

// Cave
public class Troll : IEnemy { public string Name => "Troll"; }
public class CaveMonster : IEnemy { public string Name => "Cave Monster"; }
public class CaveWeapon : IWeapon { public string Name => "Cave Weapon"; }
public class Cactus : IObstacle { public string Name => "Cactus"; }

public class CaveThemeFactory : ILevelThemeFactory
{
    public IEnemy CreateEnemy() => new Troll();
    public IEnemy CreateMinion() => new CaveMonster();
    public IObstacle CreateObstacle() => new Cactus();
    public IWeapon CreateWeapon() => new CaveWeapon();
}

// Ice
public class Yeti : IEnemy { public string Name => "Yeti"; }
public class IceMonster : IEnemy { public string Name => "Ice Monster"; }
public class IceWeapon : IWeapon { public string Name => "Ice Weapon"; }
public class Iceberg : IObstacle { public string Name => "Iceberg"; }

public class IceThemeFactory : ILevelThemeFactory
{
    public IEnemy CreateEnemy() => new Yeti();
    public IEnemy CreateMinion() => new IceMonster();
    public IObstacle CreateObstacle() => new Iceberg();
    public IWeapon CreateWeapon() => new IceWeapon();
}

class Program
{
    static void Main()
    {
        ILevelThemeFactory factory = new ForestThemeFactory();
        var boss = factory.CreateEnemy();
        var minion = factory.CreateMinion();
        var obstacle = factory.CreateObstacle();
        var weapon = factory.CreateWeapon();

        Console.WriteLine($"{boss.Name}, {minion.Name}, {obstacle.Name}, {weapon.Name}");
    }
}
#include <iostream>
#include <memory>
#include <string>

struct IEnemy { virtual std::string name() const = 0; virtual ~IEnemy() = default; };
struct IObstacle { virtual std::string name() const = 0; virtual ~IObstacle() = default; };
struct IWeapon { virtual std::string name() const = 0; virtual ~IWeapon() = default; };
struct ILevelThemeFactory {
    virtual std::shared_ptr<IEnemy> createEnemy() = 0;
    virtual std::shared_ptr<IEnemy> createMinion() = 0;
    virtual std::shared_ptr<IObstacle> createObstacle() = 0;
    virtual std::shared_ptr<IWeapon> createWeapon() = 0;
    virtual ~ILevelThemeFactory() = default;
};

// Forest
struct Goblin : IEnemy { std::string name() const override { return "Goblin"; } };
struct ForestMonster : IEnemy { std::string name() const override { return "Forest Monster"; } };
struct Tree : IObstacle { std::string name() const override { return "Tree"; } };
struct ForestWeapon : IWeapon { std::string name() const override { return "Forest Weapon"; } };
struct ForestThemeFactory : ILevelThemeFactory {
    std::shared_ptr<IEnemy> createEnemy() override { return std::make_shared<Goblin>(); }
    std::shared_ptr<IEnemy> createMinion() override { return std::make_shared<ForestMonster>(); }
    std::shared_ptr<IObstacle> createObstacle() override { return std::make_shared<Tree>(); }
    std::shared_ptr<IWeapon> createWeapon() override { return std::make_shared<ForestWeapon>(); }
};

// Cave
struct Troll : IEnemy { std::string name() const override { return "Troll"; } };
struct CaveMonster : IEnemy { std::string name() const override { return "Cave Monster"; } };
struct Cactus : IObstacle { std::string name() const override { return "Cactus"; } };
struct CaveWeapon : IWeapon { std::string name() const override { return "Cave Weapon"; } };
struct CaveThemeFactory : ILevelThemeFactory {
    std::shared_ptr<IEnemy> createEnemy() override { return std::make_shared<Troll>(); }
    std::shared_ptr<IEnemy> createMinion() override { return std::make_shared<CaveMonster>(); }
    std::shared_ptr<IObstacle> createObstacle() override { return std::make_shared<Cactus>(); }
    std::shared_ptr<IWeapon> createWeapon() override { return std::make_shared<CaveWeapon>(); }
};

// Ice
struct Yeti : IEnemy { std::string name() const override { return "Yeti"; } };
struct IceMonster : IEnemy { std::string name() const override { return "Ice Monster"; } };
struct Iceberg : IObstacle { std::string name() const override { return "Iceberg"; } };
struct IceWeapon : IWeapon { std::string name() const override { return "Ice Weapon"; } };
struct IceThemeFactory : ILevelThemeFactory {
    std::shared_ptr<IEnemy> createEnemy() override { return std::make_shared<Yeti>(); }
    std::shared_ptr<IEnemy> createMinion() override { return std::make_shared<IceMonster>(); }
    std::shared_ptr<IObstacle> createObstacle() override { return std::make_shared<Iceberg>(); }
    std::shared_ptr<IWeapon> createWeapon() override { return std::make_shared<IceWeapon>(); }
};

int main() {
    std::shared_ptr<ILevelThemeFactory> factory = std::make_shared<ForestThemeFactory>();
    auto boss = factory->createEnemy();
    auto minion = factory->createMinion();
    auto obstacle = factory->createObstacle();
    auto weapon = factory->createWeapon();

    std::cout << boss->name() << ", " << minion->name() << ", " << obstacle->name() << ", " << weapon->name() << "\n";
}

Diff: Abstract Factory v.s. Factory Method⚓︎

  • Factory Method:

    • Creates one product.
    • Relies on subclassing and overriding a factory method to decide which concrete product to create.
    • Often used when a class cannot anticipate the concrete type of object it needs to create.
  • Abstract Factory:

    • Creates families of related or dependent products.
    • Relies on object composition: the client holds a concrete factory to create consistent product families.
    • Often used when a system needs to be independent of product creation details and ensure consistency among related products.
Abstract Factory ≠ “Factory Method × N”

Abstract Factory is not simply a collection of multiple Factory Methods.

The Factory Method focuses on deferring the creation of a single product,whereas the Abstract Factory focuses on enforcing consistency among multiple related products.

The true dividing line between the two lies in whether variation is bound or independent.

  • In the Factory Method pattern, each product varies independently. Which concrete product is created is determined by the subclass, and changes to one product do not affect others.

  • In contrast, with the Abstract Factory pattern, once a concrete factory is selected,the variations of multiple products become bound together as a group.They share the same creation rules and semantic context.

By encapsulating the creation of a related set of products within a single factory object, the Abstract Factory proactively prevents invalid combinations, thereby ensuring semantic consistency and overall correctness of the product family at the system level.