Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Design Patterns  ›  Lesson

The Factory Method Pattern

GoF Design Patterns 15 minFactory Method moves the decision of which concrete class to create into one dedicated method, so the rest of the code works only with the abstraction.

The Factory Method Pattern

Shelfwise charges Indian customers through a UPI gateway and everyone else by card. The same three lines, if country == "IN": gateway = UpiGateway() else: gateway = CardGateway(), appear in checkout, refunds and subscriptions. Adding a wallet option for one country means finding and editing all three, and hoping nobody made a fourth copy. Factory Method gives that decision one home.

The rule

Put "which class do I create?" in one method that returns the abstraction.

Callers want a PaymentGateway. They should not have to know which concrete class to build, or the rule for choosing it. A factory method holds that knowledge:

def gateway_for(country) -> PaymentGateway:
    if country == "IN":
        return UpiGateway()
    return CardGateway()

gateway = gateway_for(order.country)
gateway.charge(order.payment_details, order.total())

Checkout, refunds and subscriptions each call gateway_for(...) and then use only the PaymentGateway interface. None of them names UpiGateway or CardGateway. Adding a WalletGateway means writing that class and adding one line inside gateway_for; no caller changes.

In the classic object-oriented form, the factory method is a method on a base class that subclasses override to decide what gets made:

class ReportExporter(ABC):
    def export(self, figures):
        writer = self.create_writer()   # the factory method
        writer.write(figures)

    @abstractmethod
    def create_writer(self): ...

class CsvExporter(ReportExporter):
    def create_writer(self):
        return CsvWriter()

export holds the shared steps; each subclass decides which writer to create. Both forms share the same idea: creation is separated from use, and callers depend only on the abstraction.

It pairs naturally with Dependency Inversion. DIP says checkout should receive an abstraction; a factory is often the place that decides which concrete one to hand over.

The shortcut

Search for the same if choosing which class to construct in more than one place. Move it into one function or method named for what it produces, make it return the abstraction, and replace every copy with a call.

Shelfwise's notification channelsStep 1 of 4
  1. Spot the repeated choice

    Three files contain if customer.prefers_sms: SmsSender() else: EmailSender().

The trap is wrapping every constructor in a factory. make_book(title, price) that just calls Book(title, price) adds a layer and hides nothing. A factory earns its place when there is a choice to hide.

Drills

Answer fast — trust the rule.

quick drill

What should a factory method return?

quick drill

After introducing gateway_for(country), what does adding WalletGateway require?

quick drill

In the subclass form, what does CsvExporter override?

quick drill

When is a factory method NOT worth adding?

Remember

  • Put "which class do I create?" in one method that returns the abstraction.
  • Repeated class-choosing ifs become one factory, and every copy becomes a call.
  • Trap: factories that wrap a single constructor and hide no choice.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainOne place to choose the gateway

Shelfwise creates payment gateways with if country == 'IN': gw = UpiGateway() else: gw = CardGateway() copied into checkout, refunds and subscriptions. Explain how a factory method improves this and what adding a new gateway requires afterwards.