Demystifying Design Patterns
Often times I find it beneficial to look outside of one's own industry for inspiration. There is often a lot of practical wisdom to be gained from areas that seem unrelated to our own domain, and possibly even similar problems that have already been solved. This has proven valuable to me before, when I was a software developer in the furniture industry - where I found that drawing inspiration from the fashion industry could be helpful in furniture. When working on web apps that render user-customized furniture online, I remember being particularly impressed by the user experience that Nike came out with for designing and visualizing your own sneaker - a wizard that allowed you to choose colors and styles to make your shoe your own.
I believe this can a helpful exercise for software development in general. As quickly as the industry moves - moments, fads, libraries, even entire programming languages can be fleeting. The break-neck pace of our ever-evolving standards certainly makes it hard to stop to consider that there may be wisdom in other industries, or even within the heritage of our own. Use of design patterns - general, reusable solutions to commonly occurring problems - is an area where I think some extrospection may be beneficial. Specifically, I think looking outside of our own industry can help us understand the simplicity of design patterns as a concept, and better understand their place in the problem-solving process.
The Gang of Four themselves applied ideas from Christopher Alexander's 1977 architectural book A Pattern Language in their monumental work Design Patterns. Alexander's A Pattern Language proposed that we create a language around common problems and their solutions. Although written about architecture, the idea of the work was that other industries such as interior design and engineering can apply the same methodology; and The Gang of Four's Design Patterns was their application of his idea of a common pattern language to programming. Thus, one of the most influential books in software engineering is the result of engineers embracing philosophies from other professional disciplines.
But now we're in a different world from that of the original release of the Gang of Four's book. Object oriented programming has evolved. Functional programming has taken significant steps once again towards mainstream adoption, and OOP languages are inching towards more functional practices. Libraries such as JavaScript's React, ReactiveX implementations in dozens of popular languages, and LINQ in C# apply functional thought to imperative-leaning languages. F# is a first-class functional-first language in .NET - an otherwise thoroughly object oriented ecosystem.
It seems with this shift not only the Gang of Four's object oriented patterns, but design patterns in general, are beginning to be questioned. It's not unheard of to hear the opinion that reaching for a design pattern is a recipe for adding unneeded complexity at best, or at worst perceived as a junior developer's over-zealous attempt to apply something they learned in CS school to a situation that doesn't call for it.
Now, there is plenty to be said about the wrong pattern being applied to a problem. But to say it's a problem with patterns in general is surely throwing the baby out with the bath water. It is important to realize that patterns are always a potential solution to a problem, never the solution to the problem. An approach is never going to exhaustively solve all problems; rather it should solve a problem as elegantly and thoroughly as possible while not creating too many others.
An example is the front-end JavaScript framework Angular's choice to buy wholesale into observables via RxJS. This choice was a good one I believe, but the pattern of observables does not solve everything, and to some it may not be intuitive to replace scenarios that could be solved by JavaScript's native Promise API with observables which are not native to the language. In this scenario, a pattern the Angular team chose to adopt had sweeping implications which solved many problems, but may prove to be a bit opaque in comparison to much simpler approaches.
Another reason design patterns may be falling from the good graces of some may be because our languages, tools, and libraries are so mature these days, they have the textbook patterns built into them already. The Gang of Four's prototype pattern is at the heart of JavaScript's language design. Dart implements observable streams so well that ReactiveX (a family of observable libraries for various languages) adds their functionality to the existing API rather than making a competing API. Write a Python web server and you'll be using decorator pattern no matter if you realize it or not. All of this means that, as time marches on and our tools get better, the problems being solved by the Gang of Four's design patterns are mostly solved for us - at higher levels especially.
But even if we are not reciting ideas from Design Patterns each morning in planning for the next task at hand, software engineers still have common problems to solve on a daily basis - whether object oriented or functional - just as architects, civil and mechanical engineers, and interior designers do. In any of the above professions and many more, it would be irrational to refuse to identify and apply nomenclature to a common problem that has a common solution. Just as suggested by Christopher Alexander et al. in A Pattern Language, we can benefit from having a common design language in our respective fields. By doing so we can focus on the big picture, composing known solutions to known problems, so we can spend more time solving big problems that we haven't thought of yet.
Learning from Others
There can be a certain stigma around discussing design patterns, I think, in that they come off as something that should be left behind at school and not discussed at the whiteboard in the office, or that the discussion seems haughty. If you mention a pattern, there is a possibility you'll come off as over-eager, impractical or maybe condescending. Barring some circles that may embrace them more readily, it's certainly not a given that every engineering community will have adopted patterns into their vernacular.
This, I think, is unfortunate. Design patterns are universal to all creative, problem solving disciplines. If you frown when you think of design patterns because your mind conjures an image of hipsters gathered around a whiteboard swirling scotch, twirling their mustaches and discussing the merits of using command pattern for AI behaviors in a video game, it's time for us to take a step back and adjust our expectations.
Here's the proposed idea of a pattern language from the namesake book:
Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice.
This definition is very approachable, and widely applicable to scenarios that don't require one to have a monocle and a tobacco pipe to find it useful. A pattern consists simply of identifying a common problem, describing it, describing the solution in general enough a way that it can be applied to many different situations in different ways, and naming it. Let's look at a couple examples from the nearly two hundred patterns established in A Pattern Language.
Entrance Transition: Buildings, and especially houses, with a graceful transition between the street and the inside, are more tranquil than those which open directly off the street. - A Pattern Language p.549
This pattern has a name; a common, repeatable problem; and an understandable but abstract solution to the problem. The problem is that buildings in which a cozy, homey environment is desired, there needs to be some sort of transition when entering the building so as to help the individual transition from the previous environment to the new environment. You don't want to walk off the street, take two steps and sit on a sofa in front of a fire place. There needs to be an indication that the busy street is that other place and you are entering into this new environment. To accomplish this, you apply the Entrance Transition pattern to your design by creating an area which gently introduces your guest to their new environment.
This name - problem - solution approach to defining a pattern creates a language that anyone familiar with the domain can identify the pattern by name and determine if it applies to the problem they are approaching. Even a passing familiarity with the pattern may be enough to cause one to recall it when they encounter a transition space and appreciate the design. Another example:
Indoor Sunlight: If the right rooms are facing South, a house is bright and sunny and cheerful; if the wrong rooms are facing South, the house is dark and gloomy. - A Pattern Language p.615
Again, the definition is straightforward and precise, but not too acedemic either. The name - problem - solution definintion is approachable enough and the problem is familiar enough that it can be useful not only to architects, but even house hunters who love lots of sunlight. Now let's look at a Gang of Four definition of a pattern:
Prototype: Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
- Design Patterns: Elements of Reusable Object-Oriented Software p.133
Again, this definition is structured in a way that we can quickly ascertain name - problem - solution. A quick breakdown will demonstrate that it's as user-friendly a definition as the previous architectural ones. First, prototype is an accessible name, familiar to folks acquainted with various disciplines, being applicable to things from test models in the automobile industry to character design in literature. Second, the problem is object creation, specifically creating certain types of objects. Last, the solution is that an object created via the prototype pattern is created as a copy of another object that already exists, thus inheriting its type and state.
Hopefully with this comparison of architectural pattern language and software pattern language, it's easier to see that the concept of establishing design patterns is an effort to categorize problems into a language - a vernacular - that communicates clear solutions to common problems. More importantly, I hope that it demonstrates that it is not, or at least should not, be a badge of academic superiority. Whether creating objects via prototypal inheritance or designing an entrance foyer and putting the kitchen on the South side of a house, a pattern should be practical rather than pie-in-the-sky.
Thinking in Abstractions
It's impossible to avoid design patterns. When you put RabbitMQ in your stack, you are using event queue whether one knows it or not. But contributing to and communicating in a common pattern language is a sign of a healthy, cohesive professional community. It encourages the more experienced to communicate with precision, and is an important learning mechanism for the inexperienced. Thus, I think returning to this level of communication would be beneficial to software engineering in general.
To get here though, we need to be able to identify problems, categorize them, and mentally link them with patterns. We may even need to define a pattern by establishing name - problem - solution definitions like we saw in A Pattern Language. This requires becoming familiar with and demystifying another occassionally intimidating concept: abstraction. Like patterns, abstraction is everywhere, used in all industries that deal with same problems over and over again. And also like patterns, we can look to other industries to take that cold, occasionally soulless tech industry edge off of our idea of it.
First, a definition from Greg Michaelson in his book Functional Programming Through Lambda Calculus:
"[Abstraction] involves generalization from concrete instances of a problem so that a general solution may be formulated.
Perhaps the most simple example of this is something we'd learn fairly early in math. Say we have a word problem where we have two kids having joint birthday parties and they are both bringing all of their friends, and we want to determine how many will be at the party by adding the two groups together. 10 kids will be coming for Alice's birthday, and 13 will be coming for Amy. So we have a 10 + 13 = 23. Now Alice isn't quite sure how many of her friends will come, but Amy is still sure that 13 are coming for her, so now we have a + 13 = b. This is abstraction. We've taken something concrete and we've generalized it. Finally, we account for the fact that Amy might have a different number show up: a + b = c.
The example above is extremely basic, but abstraction is as simple as that. Identify a common pattern with a concrete example, and generalize it. Returning to our contrived example of a + b = c. Any number of fields, both arts and science, can use this abstraction to describe a problem they encounter every day. The obvious example is what we discussed earlier - mathematics: number + number = number. Now consider a programming example, string + string = string. But this abstraction even applies to an artist's palette: color + color = color - something that is used every time a painter approaches their canvas.
We would struggle to find any skilled trade that does not apply abstraction in day-to-day work. Identifying common problems and solutions and thinking about them in abstractions is a skill that can be helpful to nearly everyone. Even the entrance transition pattern in architecture is an abstraction. Rather than going from one setting directly into another (street -> living room, casino -> hotel room), we recognize and apply a generalized formula to ease the transition (start -> transition area -> end).
In OOP, we think of classes or objects as our standard method of abstraction, while in functional programming a function would be. But any situation in which a pattern can be identified and a generalized solution formulated is using abstractions. Identifying a potential abstraction and creating it is not an ivory tower exercise, it's a skill that anyone can use and hone.
Towards A Pattern Language, Again
Communicating using a pattern language can benefit us in many ways if we don't neglect or overthink it. Like in other industries, it can help us communicate ideas easily, identify problems quickly, and it provides a template to base future patterns on. Utilizing the sunny side of a house to create bright, inviting spaces is not a practice that's going a way. It's a practice that has become so basic (not because it's insignificant, but because it has been identified and integrated into the common pattern language) and well known that we can do nothing but build upon and expand it as a standard.
By observing some patterns established in architecture, I hope that we have demystified design patterns as a concept and that we can think of them, not as purely intellectual concepts, but as something we do every day. As software engineers, let's continue valuing the communication and documentation and naming of patterns. It is good for posterity, it encourages continuous education among even the most seasoned of us, it gives us common ground for communication. Let's embrace it - monocles and tobacco pipes are not required.