🦆 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.DBfor a driver registered by name.image.Decode— dispatches to a decoder registered viaimage.RegisterFormat.net.Listen("tcp", addr)— returns the rightnet.Listenerfor 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.
Related patterns
Provide an interface for creating families of related objects without specifying their concrete types.
CREATIONAL BuilderConstruct a complex object step by step, separating how it's built from its final representation.
CREATIONAL PrototypeCreate new objects by cloning an existing, configured instance instead of building one from scratch.
BEHAVIORAL StrategyDefine a family of interchangeable algorithms, encapsulate each one, and select which to use at runtime.
Check your understanding
Score: 0 / 31. 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.