Design patterns aren’t design patterns

There are three reasons why the GoF patterns aren’t patterns in the Alexandrian sense. The first is the simple and acknowledged fact that their patterns are solution-oriented rather than problem-oriented. The second and more contestable point is that they are idioms of the programming languages of the time. The last is the concern that they aren’t alive. We will discuss this final point in greater detail later, but for now, consider aliveness to be the properties of self-starting, adjusting, and adapting as necessary. Living patterns engage with their neighbours, making it easier for surrounding elements to find satisfying configurations.

Solution-oriented

For many patterns, we need to introduce a starting point: the problem. All the GoF patterns are visions of a final form—a ready-selected solution. The process by which a problem is understood and resolved is the lesson we want to teach, but a solution doesn’t do this. Alexandrian design patterns are problem-oriented. They are about wisdom and experience when faced with problems encountered many times before. Solutions tell you what it looks like when it’s done, but people learn best from questions, counterexamples, or stories with conflict. People grow the most when they are invested in outcomes and aware of the stakes—when there are unresolved forces.

Even if they were patterns, not idioms, the GoF patterns lack the problem orientation of Alexandrian patterns. They lack the required tension.

All A Pattern Language[APL77] patterns are about repeating problems, not repeating solutions. This difference is critical because patterns aren’t simply advice on what to do. Patterns give you the structure and prep work for the questions you must ask yourself when encountering a problem. All the patterns in A Pattern Language provide a plan for a solution, but the opening section of each pattern is a recognition of a problem, with context and forces.

The Strategy pattern might be an actual pattern in software design, but it’s presented as a solution to the problem of wanting to specify behaviour by a parameter. What’s missing is a context in which you might want to transform existing code to such a spec. A pattern should be a solution to a compelling problem—a set of forces demanding that you deal with them. Without the context, Strategy is too similar to a variant of the Factory Method, State, or Command pattern, and it can be difficult for someone to see when you might choose one over any of the others.

Indeed, you could claim that State and Command are both extensions or specialisations of Strategy. The only requirement for State is a contract between the state object and the context object defining responsibility for change. For Command, you only need to ensure the Strategy objects contain the information about the receivers of their effect. The issue here is that the problem of generating commands and handing them to the invoker is the same problem as selecting a formatting algorithm and offloading it to the line-ending calculator. The problem gains nuance as you move to dynamically changing strategy objects, such as when you think about behaviour as state or move to include extra information about how to carry out the strategy, such as with Command. What’s gone wrong with this approach is how, by writing these as separate patterns, we have lost the option of considering strategic or stateful commands. A command could act differently based on its state as part of the strategy or template method. Separation of concerns isn’t always a good idea.

Idiomatic

But let’s talk about idioms. Some patterns are examples of how things were done at the time. To my mind, Memento, Bridge, Builder, Mediator, and Visitor are all examples of solutions that aren’t self-forming. At some point, people just agreed to work that way. Other alternatives exist, at least for the problems they intend to solve, so the patterns feel idiomatic of the languages at the time they were documented.

Idioms aren’t patterns because they’re not repeatable discoveries. They were decided or imagined, not a spontaneous consequence of their environment. For example, Memento is meant to provide a way to store the state of an object under change without violating encapsulation. The solution asks for a wide interface for the originator and a narrow interface for the caretaker. However, there is little in this pattern not dealt with by an object serialisation interface, generic for any type. It need not be aware of the originator to fulfil its role. In some languages, this is a given feature. The Memento pattern seems like an idiom that is looser than the relatively commonly understood but undocumented pattern of Serialisation.

What can reach beyond is a pattern that considers the problem behind the inclusion of Memento: the problem of state recovery. Consider a system that has copy-on-write capability and uses immutable data structures. In such a system, we can interpret Memento as an object that holds onto a reference to older object states. Unwinding the state is a trivial case of recovering the previous reference. In other systems, an object from the originator may not be enough. You may need more context to undo any changes.

If we consider Singleton, you might think this is a typical usage pattern appearing in many different places, but the form of the solution is idiomatic. Better solutions of when and how to instantiate the solitary object exist and would be preferred. But the Singleton pattern provides a stable, understandable, idiomatic, and wrong option for all to use and copy, and as with many idioms, it’s not quite bad enough to cause problems most of the time.

Not alive

The last point of contention—the claim that the patterns are not alive—is a little more subtle. Given how difficult it is to find evidence of lack of evidence, I make the following argument with limited confidence. The design patterns in the GoF book aren’t living patterns.

In this case, we’re interested in whether they are self-starting and neighbourly. The requirements for aliveness are contrary to those of an idiom. A pattern must come into existence of its own accord, usually due to emergence from a context, not simply agreed upon. You prove liveness by finding the pattern presenting itself as a novel, locally sourced solution at various times and places. In physical architecture, multiple spontaneous incarnations must be sighted for a pattern to be recognised as such. The majority of the GoF patterns seem lacking in this respect.

Outside of the group members who collected them, many listed patterns only have limited evidence of prior existence. For a problem-solution pairing to be a pattern, it should be self-forming. Not finding examples matching these patterns in the wild, outside of any environments where the GoF had influence, would indicate they could have been memes or idioms of those specific developers rather than patterns.

This is not to say the GoF patterns are not self-starting, just that there’s little evidence that many of them are. The Iterator, Strategy, State, Template Method, Flyweight, Factory Method, Prototype, Adapter, Observer, and Interpreter are all patterns you can see in code that is design pattern blind if you know what you’re looking for. For example, the C++ Standard Template Library includes and extends the core concept of an iterator as an object to a remarkably flexible final form.

But, and this is a big one, try collecting all those examples together. Find someone who does not know about the GoF patterns and ask them to sort them into groups. You might be surprised at how they collect them into different sets than someone who has read the GoF book[GoF94].

Beyond self-starting, there’s the aspect of linkage with other patterns and elements. The patterns in the GoF book are all independent and separate except where they seem dependent and inseparable. Patterns are neighbours, neither coupled nor isolated. The GoF patterns do not automatically support each other. The patterns in Domain Driven Design[DDD04] strongly support each other but are not so tightly coupled that they become part of the same pattern. The first POSA[POSA96] book refers to pattern systems and collects them together as an Alexandrian pattern language.

1

The Pattern-Oriented Software Architecture[POSA96] and Pattern Languages of Program Design[PLoPD95] series in general, but also Domain Driven Design[DDD04] and the contents of Patterns of Enterprise Application Architecture[PoEAA03] define levels for pattern application.