Skip to main content

Abstract Factory

complexitypopularity

Produce whole families of related objects through one interface, so the client never picks a concrete class and never gets a mismatched set.

The problem

You are building a furniture shop simulator. There is a family of related products - chair, sofa, coffee table - and several variants of that family: Modern, Victorian, ArtDeco. You need individual objects that match each other, because customers get remarkably cross when a Modern sofa turns up beside Victorian chairs.

Two problems compound. Nothing in the type system stops you from mixing variants, and vendors refresh their catalogs constantly, so every new family means surgery on core code.

The solution

First, declare an interface for each distinct product in the family: Chair, Sofa, CoffeeTable. Every variant of a chair implements Chair, and so on down the list.

Then declare the abstract factory itself: an interface with a creation method per product, each returning the abstract type. For every variant, write one concrete factory that implements the lot. VictorianFactory produces VictorianChair and VictorianSofa and literally cannot produce anything else. The client holds a FurnitureFactory reference and asks it for furniture, and because all the answers come from the same instance, they match by construction.

ConfiguratorShowroom (client)VictorianFactoryVictorianChairnew VictorianFactory()1new Showroom(factory)2createChair()3new VictorianChair()4createSofa()5sitOn()6
  1. 1At startup the app reads a setting and instantiates exactly one concrete factory. This is the only concrete decision in the program.
  2. 2The factory is injected into the client, which stores it as the abstract FurnitureFactory type.
  3. 3The client asks for a chair. It does not ask for a Victorian chair; it does not know that word.
  4. 4Inside the method a concrete product is built, but the signature still says it returns Chair.
  5. 5The next request goes to the same factory instance, which is what makes the sofa guaranteed to match the chair.
  6. 6The client uses the product through its abstract interface. Switch to ModernFactory tomorrow and not one line here changes.

Structure

Think of it as a matrix: product types down one axis, variants across the other. The abstract product interfaces are the rows, the concrete factories are the columns, and each factory fills in one column completely.

«interface»ChairsitOn()ABSTRACT«interface»SofalieOn()ABSTRACT«interface»FurnitureFactorycreateChair(): ChaircreateSofa(): SofaABSTRACTModernFactorycreateChair(): ChaircreateSofa(): SofaCONCRETEVictorianFactorycreateChair(): ChaircreateSofa(): SofaCONCRETEShowroomfactory: FurnitureFactoryfurnish()CLIENT
implementsusescreates

Code

Same example three ways: a showroom that furnishes a matching room without knowing which century it is decorating.

// A furniture shop that assembles sets by hand, and by luck.
class Showroom is
method furnish(style) is
if (style == "modern") then
chair = new ModernChair()
sofa = new VictorianSofa() // Oops. Nothing stops this.
else if (style == "victorian") then
chair = new VictorianChair()
sofa = new VictorianSofa()
// Every new style edits this method, and every edit
// is a fresh chance to ship a mismatched living room.
// One factory instance owns the variant, so the set always matches.
class Showroom is
private field factory: FurnitureFactory
 
constructor Showroom(factory: FurnitureFactory) is
this.factory = factory
 
method furnish() is
chair = factory.createChair()
sofa = factory.createSofa()
// A mismatched pair is now unrepresentable, not merely discouraged.
 
// Adding ArtDeco is one new class and one branch at startup:
class ArtDecoFactory implements FurnitureFactory is
method createChair(): Chair is
return new ArtDecoChair()
method createSofa(): Sofa is
return new ArtDecoSofa()
// The abstract factory declares one creation method per product
// type. Products of one variant collaborate; mixing variants is
// exactly what we are preventing.
interface FurnitureFactory is
method createChair(): Chair
method createSofa(): Sofa
 
// Each concrete factory covers one variant, end to end.
class ModernFactory implements FurnitureFactory is
method createChair(): Chair is
return new ModernChair()
method createSofa(): Sofa is
return new ModernSofa()
 
class VictorianFactory implements FurnitureFactory is
method createChair(): Chair is
return new VictorianChair()
method createSofa(): Sofa is
return new VictorianSofa()
 
// Every distinct product type gets its own base interface, and
// all variants of that product implement it.
interface Chair is
method sitOn()
 
class ModernChair implements Chair is
method sitOn() is
// Sit on something angular and slightly uncomfortable.
 
class VictorianChair implements Chair is
method sitOn() is
// Sit on something carved, tufted and heavy.
 
interface Sofa is
method lieOn()
 
class ModernSofa implements Sofa is
method lieOn() is
// Recline on clean lines and a low back.
 
class VictorianSofa implements Sofa is
method lieOn() is
// Recline on velvet and rolled arms.
 
// The client is written entirely against abstract types.
class Showroom is
private field factory: FurnitureFactory
private field chair: Chair
 
constructor Showroom(factory: FurnitureFactory) is
this.factory = factory
 
method furnish() is
this.chair = factory.createChair()
sofa = factory.createSofa() // Guaranteed to match the chair.
chair.sitOn()
 
// The variant is chosen once, at the edge of the program.
class Configurator is
method main() is
config = readApplicationConfigFile()
 
if (config.style == "modern") then
factory = new ModernFactory()
else if (config.style == "victorian") then
factory = new VictorianFactory()
else
throw new Exception("Unknown furniture style.")
 
showroom = new Showroom(factory)
showroom.furnish()

When to use it

  • Your code works with several families of related products and you do not want it bound to their concrete classes, either because the classes are not known yet or because you want room to add more.
  • A class has accumulated a set of factory methods that are starting to blur its actual responsibility. Extracting them into a standalone factory is the natural next move.

Pitfalls

  • Class inflation. A matrix of products and variants means a lot of interfaces and classes. Two products and two variants is already eight types. Make sure the compatibility guarantee is worth the paperwork.
  • The rigid product axis. Adding a product type is a breaking change to the factory interface and to every implementation of it. Design that axis before you commit.
  • Half-abstract clients. One stray cast to a concrete product and the variant leaks out of the factory and into code that was supposed to be ignorant of it.

Don't confuse it with

  • Factory Method. Abstract Factory is usually built out of factory methods; the difference is scope. One method for one product versus one object for a whole family.
  • Builder. Abstract Factory returns the product immediately. Builder deliberately spends several calls assembling one complex object before you fetch it.
  • Facade. When your only goal is hiding how subsystem objects get created, a facade may be the lighter answer. Abstract Factory earns its keep when variants are the point.

Check yourself

Question 1 of 5

What does an Abstract Factory guarantee that a pile of separate factory methods does not?