A few months ago, I said that “making everything into a design pattern is a sign that you don’t know what design patterns really are.” So now, I feel obliged to say something about what design patterns are.
Design patterns are frequently observed solutions to common problems. The idea comes from the work of Christopher Alexander in architecture; patterns are things like “rooms on both sides of a hallway” or “door on the front of a building.” There’s a lot we can unpack from this simple definition:
- Design patterns are not invented. They are observed. They aren’t about inventing (or re-inventing) wheels; they’re about noticing “I’ve put wheels on three things lately. Might be a good idea…” The first time you put wheels on something is an invention. It becomes a pattern when you observe that you’re re-inventing the wheel, and that’s a good thing. The wheel becomes part of your repertoire of solutions.
- Design patterns are not algorithms, which are specific solutions to generalized problems. Quicksort isn’t a pattern–nor is sorting itself. Patterns have more to do with how software is organized. They are more like stories, in which a problem leads to a solution that coordinates a number of different parts.
- Design patterns are often used without thinking; they feel natural, not clever, and that’s why they’re common. You’ll find them in your code; you’ll find them in the code of others. You can find them even if they weren’t put there consciously; patterns are often no more than a common solution to a certain kind of problem, something that looks obvious in retrospect. Patterns become a problem when programmers try to force the issue–to use patterns where they don’t quite fit, because they’ve heard that design patterns make their code better.
- Design patterns aren’t inherently good–and if you read Alexander, you’ll find that there are plenty of architectural patterns that he really doesn’t like. A corridor with rooms on both sides is a solution to certain architectural problems. It is frequently found in very boring hotels and offices.
- “Anti-patterns” may be worth avoiding, but that doesn’t mean they aren’t patterns. Frequently observed bad solutions to common problems are still frequently observed solutions to common problems. And sometimes, anti-patterns will be the best possible solution to an otherwise intractable problem. That’s the kind of technical debt that enables you to ship, as Kevlin Henney and Ward Cunningham have written.
There isn’t any magic here. While the book Design Patterns: Elements of Reusable Object-Oriented Software by Gamma, Helm, Johnson, and Vlissides (the “Gang of Four”) is a classic, design patterns really aren’t things you look up in books. Design patterns are things you find in your code; they’d probably be there whether or not they had a name. So what’s the value?
The biggest value in design patterns is that it gives us a common language for talking about software and how it’s organized. That’s why Alexander named one of his books A Pattern Language. We’ve all spent hours making diagrams on black- or white-boards to show how some software we’re writing is organized. Design patterns give a common vocabulary so that we can discuss software with some certainty that we all mean the same thing. I eventually realized that UML had the same aim: UML diagrams are like architectural blueprints, in which one kind of line represents a brick wall, another wood, another plasterboard. Unfortunately, UML was never quite standard enough, and like design patterns, was perceived as a good in itself. In the end, a common vocabulary (whether a pattern catalog or UML) is a tool, and any tool can be abused.
Since the Gang of Four, design patterns have been associated with object-oriented programming, but the idea that patterns aren’t applicable to functional languages is itself meaningless. It’s certainly true that in functional languages, some well-known patterns (like strategy or map/reduce) are either primitives or simple library functions; but saying that there aren’t patterns in functional programming is equivalent to saying that there are no common solutions to common problems. It’s still useful to point out that the “strategy” pattern is equivalent to passing a function as a parameter to another function. That language gives you an intuitive and descriptive way to discuss solutions to a problem.
Patterns change over time, as problems change; there’s nothing special to the patterns the Gang of Four observed in the 1990s. In the 2020s, we should be building a pattern language for concurrent programming–and we may find that our patterns for the enterprise software of the 90s are less relevant.
You may find that you use patterns without thinking about it; you may discover patterns in your code; you may realize you’re facing a problem that a pattern will help you to solve; or you may use patterns to describe solutions to someone else. When solving a problem, sometimes the only thing you’re missing is a name; having a name for your solution crystallizes it. Studying patterns is useful because it gives you a larger vocabulary with which to think about problems and solutions. But using patterns for their own sake leads you nowhere. I remember hearing about programmers boasting about how many Gang of Four patterns they used, and managers telling programmers to use more patterns. That’s not productive.
Like any good thing in programming, using design patterns should help you solve complex problems more simply. But there’s no guarantee that they’ll do that. And if you find that they aren’t, then you should research for some other solutions.