Factory Method Pattern
Intent:
Define an interface for creating an object, but let subclasses decide which class to instantiate.
When to Use:
When a class can't anticipate the class of objects it must create, or when subclasses should specify the objects they create.
Key Characteristics:
- Decouples object creation from usage
- Promotes Open/Closed Principle (easy to add new types)
- Client code works with interfaces, not concrete classes
Example (Chess: Knight Upgrade):
# character_type.py from enum import Enum, auto class CharacterType(Enum): KNIGHT = auto() KNIGHT_V2 = auto()# character_type.py from enum import Enum, auto class CharacterType(Enum): KNIGHT = auto() KNIGHT_V2 = auto()
# character_factory_abstract.py from abc import ABC, abstractmethod class CharacterFactoryAbstract(ABC): @abstractmethod def create_character(self): pass# character_factory_abstract.py from abc import ABC, abstractmethod class CharacterFactoryAbstract(ABC): @abstractmethod def create_character(self): pass
# character_abstract.py from abc import ABC, abstractmethod class CharacterAbstract(ABC): @abstractmethod def attack(self): pass# character_abstract.py from abc import ABC, abstractmethod class CharacterAbstract(ABC): @abstractmethod def attack(self): pass
# knight.py class Knight(CharacterAbstract): def attack(self): print("attack with sword") class KnightV2(CharacterAbstract): def attack(self): print("attack with sword++")# knight.py class Knight(CharacterAbstract): def attack(self): print("attack with sword") class KnightV2(CharacterAbstract): def attack(self): print("attack with sword++")
# knight_factory.py class KnightFactory(CharacterFactoryAbstract): def create_character(self, version): if version == CharacterType.KNIGHT: return Knight() elif version == CharacterType.KNIGHT_V2: return KnightV2() else: raise ValueError("Unknown version")# knight_factory.py class KnightFactory(CharacterFactoryAbstract): def create_character(self, version): if version == CharacterType.KNIGHT: return Knight() elif version == CharacterType.KNIGHT_V2: return KnightV2() else: raise ValueError("Unknown version")
# main.py factory = KnightFactory() knight = factory.create_character(CharacterType.KNIGHT) knight.attack() knight_v2 = factory.create_character(CharacterType.KNIGHT_V2) knight_v2.attack()# main.py factory = KnightFactory() knight = factory.create_character(CharacterType.KNIGHT) knight.attack() knight_v2 = factory.create_character(CharacterType.KNIGHT_V2) knight_v2.attack()
Output:
attack with sword attack with sword++
Summary:
- Client asks factory for an object, not knowing its concrete type
- Easy to add new product types without changing client code