Creational pattern · Gang of Four · Beginner

Factory Method

Define an interface for creating an object, but let the implementation decide which concrete type to instantiate.

Also known as — Virtual Constructor

Creational Beginner Complete

🦆 Analogy

You ask a toy shop for “a duck.” You don’t say how to build it or which exact model — you name what you want, and the shop hands you one that quacks. Swap the shop and “a duck” might come out as a rubber squeaker instead. You program against “duck,” not against a specific factory floor.

The problem

When code does d := &MallardDuck{}, it’s welded to that concrete type. Every place that constructs one must change if you add a RubberDuck, and tests can’t substitute a fake. A factory moves the new decision behind a function that returns an interface, so callers depend only on behavior.

Idiomatic Go — the constructor & the Simple Factory

The everyday Go factory is a function returning an interface. A Simple Factory extends that with a switch when the caller chooses a kind at runtime. Edit and Run:

package main

import "fmt"

// Product: callers depend on this, not on concrete types.
type Duck interface{ Quack() }

type MallardDuck struct{}

func (MallardDuck) Quack() { fmt.Println("Quack") }

type RubberDuck struct{}

func (RubberDuck) Quack() { fmt.Println("Squeak") }

type DecoyDuck struct{}

func (DecoyDuck) Quack() { fmt.Println("...silence...") }

// The factory: ask for a kind, get back a Duck.
func NewDuck(kind string) (Duck, error) {
	switch kind {
	case "mallard":
		return MallardDuck{}, nil
	case "rubber":
		return RubberDuck{}, nil
	case "decoy":
		return DecoyDuck{}, nil
	default:
		return nil, fmt.Errorf("unknown duck: %q", kind)
	}
}

func main() {
	for _, kind := range []string{"mallard", "rubber", "decoy"} {
		d, err := NewDuck(kind)
		if err != nil {
			fmt.Println("error:", err)
			continue
		}
		fmt.Printf("%-8s -> ", kind)
		d.Quack()
	}
}

🐹 Simple Factory isn't quite the GoF pattern

The duck switch above is a Simple Factory — a handy idiom, but not formally one of the 23. The real Factory Method makes the creation step itself something each context overrides. Here it is with a pizza store.

Factory Method proper — the pizza store

The order workflow is fixed — prepare, bake, cut, box — but which pizza gets created is a step each store provides. In Go (no subclassing), the base store delegates creation to an embedded creator, and NYPizzaStore/ChicagoPizzaStore each supply their own CreatePizza:

classDiagram
  class PizzaStore {
    <<interface>>
    +OrderPizza(kind) Pizza
  }
  class NYPizzaStore {
    +CreatePizza(kind) Pizza
  }
  class ChicagoPizzaStore {
    +CreatePizza(kind) Pizza
  }
  class Pizza { <<interface>> }
  PizzaStore <|.. NYPizzaStore
  PizzaStore <|.. ChicagoPizzaStore
  NYPizzaStore ..> Pizza : NY-style
  ChicagoPizzaStore ..> Pizza : Chicago-style
// OrderPizza is the fixed workflow; CreatePizza is the factory method.
func (s *PizzaStoreBase) OrderPizza(kind PizzaType) (Pizza, error) {
	pizza, err := s.store.CreatePizza(kind) // varies per store
	if err != nil {
		return nil, err
	}
	pizza.Prepare()
	pizza.Bake()
	pizza.Cut()
	pizza.Box()
	return pizza, nil
}

Each store implements CreatePizza to build its own regional pizzas — same order flow, different products.

In the standard library

  • sql.Open(driver, dsn) — returns a *sql.DB for a driver registered by name.
  • image.Decode — dispatches to a decoder registered via image.RegisterFormat.
  • net.Listen("tcp", addr) — returns the right net.Listener for the network.

Pitfalls

⚠️ A factory for one type is just indirection

If there’s exactly one implementation, NewThing() returning a concrete struct is fine — don’t wrap it in an interface and a switch “just in case.” Add the factory when a second implementation (or a test fake) actually shows up. And prefer returning the concrete type from constructors when you can — “accept interfaces, return structs.”

When to use it — and when not

✅ Reach for it when

  • Callers should depend on an interface, not on concrete types — so you can swap implementations freely.
  • You want a single place that decides which concrete type to build (and can validate the request).
  • Construction needs to vary by context — a NY store builds NY pizzas, a Chicago store builds Chicago ones.

⛔ Think twice when

  • There's only one implementation and no prospect of another — a plain struct literal is clearer.
  • You're adding a factory purely speculatively; introduce it when variation actually appears.

Check your understanding

Score: 0 / 3

1. What is the idiomatic 'factory' in everyday Go?

Go's everyday factory is just a constructor function returning an interface — callers get behavior without naming the concrete type.

2. How does a 'Simple Factory' differ from the GoF 'Factory Method'?

Simple Factory is one function/method with a switch. Factory Method makes creation an overridable step so different creators (NY vs Chicago store) build different products through the same workflow.

3. Which standard-library function is a factory?

sql.Open looks up a registered driver by name and returns a handle — you depend on the name, not the concrete driver type. image.Decode and net.Listen are similar.