RizTech Academy logo
RizTech Academy
Design Patterns in JavaScriptLesson 1 of 625 min

What a design pattern really is

A design pattern is a name for a solution you were going to arrive at anyway. Almost everything that goes wrong with patterns comes from forgetting that.

In 1994 four authors catalogued twenty-three arrangements that kept appearing in real systems. They were not inventing them — they were writing down what good programmers already did, so those programmers could say "decorator" instead of describing the arrangement every time.

Most of the catalogue was about a language JavaScript is not

This is the part that makes patterns in JavaScript different, and it is worth getting straight before the rest of the module.

The original book was written for C++ and Smalltalk — languages where a function is not a value. If you wanted to pass behaviour around, you had to wrap it in an object, and a great many patterns exist to do exactly that.

JavaScript has first-class functions and closures. So:

// Strategy, in the book: an interface, and a class per algorithm.
// Strategy, in JavaScript:
const byDate = (a, b) => a.date.localeCompare(b.date);
orders.sort(byDate);
// Command, in the book: an interface with one execute() method.
// Command, in JavaScript:
const undo = () => state.restore(snapshot);
// Singleton, in the book: a chapter.
// Singleton, in JavaScript: a module.
export const rates = await loadRates();

Three patterns, three lines. They are the same patterns — the same separation of what varies from what does not — but the machinery has disappeared, because the language does natively what the machinery was simulating.

So a JavaScript developer who writes a StrategyFactory class is usually not being sophisticated. They are writing Java in JavaScript.

Patterns are vocabulary

The value is the shared word. Explaining this to a colleague:

"I want a function that wraps another function, does something before or after calling it, and returns something with the same signature so several can stack."

Or:

"A decorator."

A paragraph becomes two syllables. That is the whole benefit, and it is a real one — most of your career is spent reading and discussing code, not writing it.

They are noticed, not planned

The code gets written because the problem demanded it, and afterwards somebody says "that is a strategy". The failure mode is the reverse: deciding to use a pattern and bending a problem to fit.

// Three files and an interface, to add two numbers.
class AddOperation { apply(a, b) { return a + b; } }
class OperationFactory {
  static create(kind) {
    if (kind === "add") return new AddOperation();
    throw new Error(`unknown: ${kind}`);
  }
}
const total = OperationFactory.create("add").apply(a, b);

versus

const total = a + b;

The first is not more professional. It is four indirections a reader must follow to learn that the code adds two numbers, and it will be extended the day somebody needs subtraction — which may be never.

A pattern used where it is not needed is worse than no pattern, because it costs the reader everything a pattern costs and buys them nothing.

The test: what varies?

Every useful pattern isolates something that changes from something that does not.

Pattern What it lets vary
Strategy the algorithm
Factory what gets created
Decorator what happens around a call
Observer who gets told
Module what is public

Before reaching for one: what is varying here, and is it varying today?

If nothing is varying, you do not have a pattern. You have indirection.

If something might vary later — and it usually might — the honest answer is still to wait. In JavaScript, refactoring to a strategy is replacing a hardcoded call with a parameter, which takes about a minute. Guessing wrong now costs every reader until then, and the guess is frequently wrong: the axis you expected to move stays fixed and another one does.

You have used them since module 1

[3, 1, 2].sort((a, b) => a - b);        // strategy, as an argument
button.addEventListener("click", save); // observer
const api = (() => { … })();            // module pattern
fetch(url).then(r => r.json());         // chain of responsibility, loosely
Object.freeze(config);                  // an immutable decorator, in spirit

Array.prototype.map takes a strategy. addEventListener is observer. JSON.parse's reviver argument is a strategy. Every async function returning a promise is a variation on the future.

You are not learning something new. You are learning the names of things you already do.

The ones worth knowing here

Five, chosen because a JavaScript developer meets all of them in their first month:

  • Module — the oldest JavaScript pattern, and what ES modules formalised.
  • Factory — making objects when a constructor is not enough.
  • Strategy — which is usually just a function.
  • Observer — which is how the entire browser works.
  • Decorator — wrapping without changing the shape.

The other eighteen are worth reading about one day. None is worth memorising now.

Check your work

What a pattern is: a name for a solution you would arrive at anyway.

Why JavaScript needs fewer of them: first-class functions and closures do natively what much of the original machinery simulated.

What a StrategyFactory class usually means: somebody writing Java in JavaScript.

The real value: shared vocabulary, which matters because most of the job is reading and discussing code.

Why patterns are noticed, not planned: the problem demands the code; the name comes afterwards.

Why a needless pattern is worse than none: it costs the reader everything and buys nothing.

The test: what varies, and is it varying today?

Why waiting is usually right: refactoring to a strategy in JavaScript takes a minute, and the axis you predicted usually is not the one that moves.

Where you have already met them: sort, map, addEventListener, an IIFE.

Practice

  1. Find three places in your own code where you pass a function as an argument. Each is a strategy — say what varies in each.
  2. Open MDN for Array.prototype.sort and identify the strategy in its signature.
  3. Write the OperationFactory example and then delete it. Say what was lost.
  4. Look at your capstone's state.js and name the pattern it uses without adding anything.
  5. Count how many addEventListener calls are in your capstone. Each is an observer.
  6. Write a strategy as a class with one method, then as an arrow function. Compare the line counts.
  7. Find a place where you added flexibility that has never been used. Remove it.
  8. Explain "decorator" to somebody without using the word.
  9. Find a pattern in the browser APIs this lesson did not mention.
  10. Argue the case against teaching patterns at all in JavaScript. Then say what you would keep.

Next: the module pattern, and what closures replaced.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship