Factory Method Pattern

The Factory Method is a creational design pattern that defines an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created. It promotes loose coupling by delegating the instantiation logic to subclasses.

Key Principles

  • Encapsulate object creation: Hide "how" objects are created.
  • Open/Closed Principle: Extend behavior (new product types) without modifying existing code.
  • Dependency Inversion: Depend on abstractions (factory interface), not concrete classes.

Structure

  • Product: Interface/abstract class for objects the factory creates.
  • Concrete Product: Specific implementations.
  • Creator (Factory): Declares the factory method (returns Product).
  • Concrete Creator: Overrides factory method to return specific Concrete Product.

Python Example: Shape Factory

from abc import ABC, abstractmethod
import math

# Product Interface
class Shape(ABC):
    @abstractmethod
    def area(self) -> float:
        pass

# Concrete Products
class Circle(Shape):
    def __init__(self, radius: float):
        self.radius = radius

    def area(self) -> float:
        return math.pi * self.radius ** 2

class Rectangle(Shape):
    def __init__(self, width: float, height: float):
        self.width = width
        self.height = height

    def area(self) -> float:
        return self.width * self.height

class Square(Shape):
    def __init__(self, side: float):
        self.side = side

    def area(self) -> float:
        return self.side ** 2

# Creator (Factory) with Factory Method
class ShapeFactory(ABC):
    @abstractmethod
    def create_shape(self) -> Shape:
        pass

    # Template method using the factory
    def compute_area(self) -> float:
        shape = self.create_shape()
        return shape.area()

# Concrete Creators
class CircleFactory(ShapeFactory):
    def __init__(self, radius: float):
        self.radius = radius

    def create_shape(self) -> Shape:
        return Circle(self.radius)

class RectangleFactory(ShapeFactory):
    def __init__(self, width: float, height: float):
        self.width = width
        self.height = height

    def create_shape(self) -> Shape:
        return Rectangle(self.width, self.height)

class SquareFactory(ShapeFactory):
    def __init__(self, side: float):
        self.side = side

    def create_shape(self) -> Shape:
        return Square(self.side)

# Usage (client code)
if __name__ == "__main__":
    circle = CircleFactory(radius=5).compute_area()
    rectangle = RectangleFactory(width=4, height=6).compute_area()
    square = SquareFactory(side=3).compute_area()

    print(f"Circle area: {circle:.2f}")      # ~78.54
    print(f"Rectangle area: {rectangle}")   # 24
    print(f"Square area: {square}")         # 9

When to Use Factory Method

  • When a class can't anticipate the type of objects it needs to create.
  • When you want to localize object creation knowledge in subclasses.
  • To extend with new product types easily (add new factory subclass).

Summary: Factory Method lets subclasses decide which object to instantiate, keeping client code decoupled from concrete classes.