Creational Patterns II: Abstract Factory & Builder

Design Patterns

Chapter 3 · Creational Patterns II: Abstract Factory & Builder

Chapter 2's Factory Method delegated creating one object to a subclass. This chapter covers two bigger jobs: keeping a whole family of related objects consistent (Abstract Factory), and constructing one genuinely complex object safely, step by step (Builder) — both demonstrated by reproducing the real bug each one exists to prevent.

Abstract Factory: Keeping a Family of Objects Consistent

A UI toolkit needs a button and a checkbox that always match — a light-theme button should never end up next to a dark-theme checkbox. Abstract Factory provides one factory per family, so requesting any piece of a family automatically gets the matching version of every other piece.

class UIFactory(ABC): @abstractmethod def create_button(self): pass @abstractmethod def create_checkbox(self): pass class LightThemeFactory(UIFactory): def create_button(self): return LightButton() def create_checkbox(self): return LightCheckbox() class DarkThemeFactory(UIFactory): def create_button(self): return DarkButton() def create_checkbox(self): return DarkCheckbox()
Verified directly — each factory produces a genuinely consistent family
build_ui(LightThemeFactory()) returns a LightButton and a LightCheckbox — confirmed by type, not just by name. build_ui(DarkThemeFactory()) returns a DarkButton and a DarkCheckbox. Every single component produced through a given factory belongs to the same family, every time.
The real bug this actually prevents — reproduced directly
Bypassing the factory and constructing components individually, exactly the way an unwary developer might: LightButton() paired with DarkCheckbox(). This runs without any error and compiles/executes perfectly — 'Light button (white bg, black text)' next to 'Dark checkbox (black bg, white check)' — a genuinely mismatched, jarring UI, produced by code that looks completely reasonable at a glance. Abstract Factory's real value is structural: going through build_ui(factory) makes this specific mistake impossible to write, not just discouraged by convention.

Builder: Constructing a Complex Object Safely

A class with many optional parameters — a computer configuration, say — tempts a "telescoping constructor": one big constructor accepting everything positionally.

class Computer: def __init__(self, cpu, ram, storage, gpu=None, has_wifi=True, has_bluetooth=True, case_color='black'): # ... store each parameter ...
The real bug this actually causes — reproduced directly
Intending cpu='i7', ram='16GB', storage='512GB SSD', case_color='white', a caller writes Computer('i7', '16GB', '512GB SSD', 'white') — a completely natural-looking mistake, since 'white' lands in the fourth positional slot, which is actually gpu, not case_color. The result: gpu='white' (nonsensical — a GPU with no such model) and case_color silently stays at its default, 'black' — exactly the opposite of what was intended, with no error raised anywhere.

Builder replaces positional parameters with named, chained method calls — each step says exactly what it's setting:

class ComputerBuilder: def set_cpu(self, cpu): self._cpu = cpu; return self def set_ram(self, ram): self._ram = ram; return self def set_storage(self, storage): self._storage = storage; return self def set_gpu(self, gpu): self._gpu = gpu; return self def set_case_color(self, color): self._case_color = color; return self def build(self): return Computer(self._cpu, self._ram, self._storage, self._gpu, case_color=self._case_color)
Verified directly — the identical intent, correctly built, with no possible mix-up
ComputerBuilder().set_cpu('i7').set_ram('16GB').set_storage('512GB SSD').set_case_color('white').build() produces exactly the intended object: gpu=None (correctly left unset — the step was never called) and case_color='white' (correctly set) — the exact opposite of the telescoping constructor's own mistaken result, for the identical underlying intent. There is no positional slot to get wrong, because every step names the field it sets.

Where This Connects

This chapter's findingWhat it sets up
Abstract Factory structurally preventing a mixed-family bugThe same "make the wrong thing impossible to write, not just discouraged" idea reappears in Chapter 5's Composite
A real, reproduced telescoping-constructor bug, fixed by BuilderA concrete case study for Clean Code, SOLID & Refactoring's own material on function/constructor parameter design
Both patterns building on Chapter 2's Factory MethodChapter 4's structural patterns shift focus from *creating* objects to *composing* already-created ones

Hands-On Exercises

Exercise 1

Using this chapter's own Abstract Factory pattern, add a third family member — create_slider() — to UIFactory, LightThemeFactory, and DarkThemeFactory, with matching LightSlider/DarkSlider classes. Verify build_ui()-style code produces a consistently-themed slider alongside the button and checkbox.

📄 View solution
Exercise 2

Using this chapter's own telescoping-constructor bug, identify exactly which named parameter each of the four positional arguments in Computer('i7', '16GB', '512GB SSD', 'white') actually binds to, and explain why the resulting object doesn't raise any error despite being wrong.

📄 View solution
Exercise 3

Using this chapter's own two patterns, explain what specific kind of bug each one prevents — Abstract Factory vs. Builder — and why a class with only a single required parameter and no optional ones would need neither pattern.

📄 View solution

Chapter 3 Quick Reference

  • Abstract Factory: one factory produces a whole family of related objects — verified consistent by type (LightButton+LightCheckbox, DarkButton+DarkCheckbox)
  • Verified directly: bypassing the factory makes mixing families (LightButton+DarkCheckbox) trivially possible with no error — exactly what Abstract Factory structurally prevents
  • Builder: named, chained construction steps replace a positional "telescoping constructor"
  • Verified directly: a realistic positional-argument mistake silently set gpu='white' instead of case_color='white', with no error raised — the Builder version produced the correct result with no possible mix-up
  • Next chapter: Structural Patterns I — Adapter and Facade, shifting from creating objects to composing already-created ones