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.
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.
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.
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.