Zachary W. Huang
December 21, 2022
Note: This is basically everything I wanted to say in a preface to my guide on GoF Design Patterns, but I figured it would be better as its own blog post.
Software design patterns didn’t appear out of nowhere. Here’s a quick rundown of how they came to be (please forgive me if I’m incorrect about any of the following).
In the 1960s and 1970s, architect Christopher Alexander began publishing books on his work and research in an attempt to improve the state of architecture at the time, which he deemed ugly and “hollow” [1]. His Notes on the Synthesis of Form (1964) discussed the process of design and the effect of forces on form, while A Pattern Language (1977) and A Timeless Way of Building (1979) introduced a generative pattern language that was intended to produce structures which are whole and “alive” [2] [3] [4]. Many of Alexander’s ideas, unbeknownst to him, were picked up by the object-oriented programming community and applied to software development.
Before Alexander even knew what object-oriented programming was, his name had become “a household word” in the OOP community [5]. His ideas had inspired a series of essays on software design by Richard P. Gabriel throughout the 90s (which were compiled into the 1996 book Patterns of Software) [5]. In addition, Alexander’s pattern language directly inspired the idea of software design patterns, many of which were documented in the Gang of Four’s hugely popular Design Patterns: Elements of Reusable Object-Oriented Software (1994) [6]. Roll credits!
However, that’s not the end of the story.
If you think about it, you can’t exactly take an idea from architecture (especially one that, at the time, had not yet even proven successful) and expect it to work perfectly in a completely different field. Alexander, in the forward to Patterns of Software, states:
“I am not a programmer, and I do not know how to judge programs. But, speaking only about what appears in this book, I must confess to a slight—reluctant—skepticism. … I cannot tell, as yet, whether the probing questions asked in this book, will actually lead to better programs, nor even what a better program is.” [5]
In GoF’s Design Patterns, the authors acknowledge the similarities and differences between their patterns and those in Alexander’s pattern language. They state:
“When Alexander claims you can design a house simply by applying his patterns one after another, he has goals similar to those of object-oriented design methodologists who give step-by-step rules for design.” [6]
“From Alexander’s point of view, the patterns in this book do not form a pattern language. Given the variety of software systems that people build, it’s hard to see how we could provide a “complete” set of patterns, one that offers step-by-step instructions for designing an application.” [6]
However, Alexander seems to think that the proponents of design patterns in software may have missed the point of his pattern language. In a keynote speech to the 1996 OOPSLA convention, Alexander stated:
“I think that insofar as patterns have become useful tools in the design of software, it helps the task of programming in that way. It is a nice, neat format and that is fine. However, that is not all that pattern languages are supposed to do. The pattern language that we began creating in the 1970s had other essential features. First, it has a moral component. Second, it has the aim of creating coherence, morphological coherence in the things which are made with it. And third, it is generative: it allows people to create coherence, morally sound objects, and encourages and enables this process because of its emphasis on the coherence of the created whole.” [7]
“I understand that the software patterns, insofar as they refer to objects and programs and so on, can make a program better. That isn’t the same thing, because in that sentence “better” could mean merely technically efficient, not actually “good.” Again, if I’m translating from my experience, I would ask that the use of pattern language in software has the tendency to make the program or the thing that is being created is morally profound—actually has the capacity to play a more significant role in human life. A deeper role in human life. Will it actually make human life better as a result of its injection into a software system?” [7]
If I had to answer this last question, it would be a resounding “…it depends”. I mean, this is just a fundamental difference between architecture and software development - generally, “better” architecture is likely improve human lives, but “better” software is simply better in the sense that it is more reusable, efficient, robust, etc. The same design patterns used in a program that detects cancer can also be used in a program that guides ICBMs to their destination. That’s just the nature of software.
Well, yeah. They’re no silver bullet, but they can help you understand how you can organize code to improve reusability and extensibility. In general, though, knowing which patterns work and why is likely better than throwing as many as you can into a program (see: Enterprise FizzBuzz). Like anything else, design patterns are meant to be used in moderation. But if you know how you want to structure a program, you can use patterns as a starting point, which will save you the time and effort of reinventing the same solution (or a worse one).
You should note that these design patterns are mainly meant to be used when you’re dealing with (mostly) pure object-oriented programming. Since the 1990s, it seems like the paradigm has started to shift towards functional programming patterns. Functional features/ideas like anonymous functions (lambdas), streams, record types, etc, have been incorporated into languages like Java and C++. In addition, functional languages often offer alternative solutions to the problems that design problems claim to solve. For example, in most languages, first class functions can potentially replace any pattern that encapsulates executable code (such as Command and Strategy). In addition, multiple dispatch (found in Julia and Common Lisp’s CLOS) essentially makes the Visitor pattern useless. The algebraic data types found in Haskell and the ML family also offer a cleaner approach to Composite objects, and reactive programming frameworks (Svelte comes to mind) abstract away the Observer pattern completely.
However, let’s not get too ahead of ourselves. The world still runs on Java and C++ [citation needed], so we might as well learn about the patterns that keep OOP code organized and clean.
If you want to look more into Christopher Alexander’s work, his latest work is a four-part book series called The Nature of Order [1]. As far as I can tell, it is the best summary of his life’s work, and it introduces many interesting ideas about life and design. Unfortunately, Christopher Alexander passed away in March of 2022 at the age of 85. However, he leaves behind an extraordinary legacy. One interesting project that may be worth looking into is Alexander’s Beautiful Software Initiative, which seeks to address the moral component of his architectural philosophy in the context of computer science.
In my opinion, design patterns will likely stay relevant as long as the popular OOP languages do (C++, Java, etc). Which is the say, a pretty long time. But ideas from functional programming (and other paradigms) are definitely having an effect on modern programming languages such as Rust (even Python has pattern matching nowadays). And I would expect that these new features will displace the OOP design patterns that solve the same problem.