Another problem with problems with design patterns
The documentation for a design pattern describes the context in which the pattern is used, the forces within the context that the pattern seeks to resolve, and the suggested solution. [For example:] The visitor design pattern is a way of separating an algorithm from an object structure. A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures.
I have read the view that design patterns represent deficiencies in a programming language. This argument stems from the observation that the specific code examples for half or more of the patterns in the GoF Book are "superfluous" in languages that provide abstractions above and beyond basic OO. The Visitor pattern can be said (depending on which Golden Hammer you are holding) to represent a way to brew your own multiple dispatch, open classes, or pattern matching.
Fools ignore complexity. Pragmatists suffer it. Some can avoid it. Geniuses remove it.
Does this mean that given a sufficiently expressive language there will be no design patterns? Certainly not. Each design pattern organizes a problem and
one suggested solution in a structured way, giving the solution a name so that programmers may enjoy a common vocabulary. The pattern would only go away if the problem goes away.
But why would a more expressive language have this better feature, if not to solve the problem? Clearly the problem doesn’t go away, it is just that instead of writing our own incomplete and buggy mechanisms for multiple dispatch we can now use the language’s complete, tested, and “free” multiple dispatch feature to solve the problem.
If you consider the problem to be “How do I implement my own multiple dispatch,” then the Visitor pattern is obsolete if you are using a language like Common Lisp. However, if you consider the problem to be “How do I separate algorithms from data structures,” then the only thing obsolete in the originally publicized pattern are the language-specific details. The high-level approach can still be relevant.
A programming language is low level when its programs require attention to the irrelevant.
Some programming languages accomplish the same task with a declaration or a singe line of code. From this, some draw the conclusion that design patterns exist because popular programming languages lack certain abstractions or they lack the meta-abstractions permitting you to build your own abstractions cleanly. The underlying assumption here is that the purpose of the pattern is to explain specifically how to write the code.
This is clearly not the case. The Ruby language implements the Singleton pattern with a single abstraction, the Singleton module.
include Singleton
is all you need to implement the pattern. Yet, would we say that in Ruby there are no Singletons? No, we would say that Ruby makes them easy.
I always felt the beauty of Java was that there is only one ‘standard’ way of handling most problems. I know it might not be ‘fun’, ‘smart’ or ‘elegant’ in most people’s views, but most software factories don’t want people to write elegant code. They want standardized maintainable stuff that people can understand ten years from now because it’s written in the same fashion [as] everything else out there…
I really think that great code is easy to understand and follows patterns that people are used to and comfortable with… I read someone’s code I just want to be able to understand it as quickly as possible, find the spot where the changes go and be able to figure out how said changes should fit in the general design.
One argument is that implementing code a certain way is what makes it easy to read, what makes it
familiar. This line of thinking is grounded in the idea that the valuable part of the patterns is the suggested solution at a detailed level, because that is what will enable people to read the code and say to themselves, “I know what that does.”
The counter-argument is that designing architecture a certain way with familiar names is what makes it easy to understand. This line of thinking is grounded in the idea that the valuable part of the patterns is the identification of the context, forces, and resolution, because that is what will enable people to read the code and say to themselves, “I know what that
means.”
A Pattern Language: Towns, Buildings, Construction is one of the most loved books on my shelf. Anyone who has an interest in making their house or apartment a deeply satisfying, functional home needs to read this book once in their lives. If you don’t have the budget to buy it today, I urge you to borrow it from a friend or from your library.
And of course, you can learn a great deal about software design from it. In many ways, you can learn much more about how to think about design than from software patterns books. One thing that stands out is that this is a Timeless Way of Building. When you see how these patterns transcend specific construction materials and methods, you the common forces in software design that transcend tools.
Even if the exact implementation of a pattern is abbreviated or even trivial in another language, patterns still provide the identification of the context, forces, and resolution with a common name everyone can understand.
A programming language
can make some patterns go away. But not by providing features and abstractions that eliminate the repetitive boilerplate. Such languages make the patterns succinct, elegant, and remarkably easy to read… but not superfluous.
Instead, a language can make the patterns go away by introducing entirely different contexts for designing programs. To stretch the building metaphor, patterns of home design go away if you are designing a commune instead of separate family dwellings. It is not a question of how one builds homes, we do away with homes altogether.
Radically different paradigms for programming can eliminate the patterns we know. For example, if you are building a Common Lisp program with objects and use multiple dispatch, you are still thinking in objects and thus you wrestle with the same forces upon them.
But if you build a Common Lisp program in a functional way, you may be able to get by without any objects at all, not even objects you have faked using closures. In that case, you don’t need the OO patterns because you aren’t in an OO context.
But that being said, there are
other patterns you need that suit a functional programming context like
Iterator and
Idiomatic Traversal. It is not a case of Common Lisp being so powerful that no patterns are needed, it is a case of Common Lisp providing the power to make certain patterns elegant and simple should you choose to program in a manner that can make use of them.
So languages can make patterns go away by introducing entirely new paradigms. And those paradigms will have their own patterns appropriate for resolving their forces.
In summary, I dispute that design patterns represent language flaws. I claim that while the specific suggested implementations may work around language flaws, the context, forces, and resolution are relevant and valuable as long as you are working within the same paradigm.
Get into a rut early: Do the same process the same way. Accumulate idioms. Standardize. The only difference(!) between Shakespeare and you was the size of his idiom list — not the size of his vocabulary.
All quotes from Wikipedia or from Alan J. Perlis’
Epigrams in Programming unless otherwise attributed.
post scriptum:
I notice that the first couple of comments that appeared on a popular link aggregation site disagreed with my perspective that design patterns are much more than the language-specific implementation. To them, a Singleton in Ruby is a library call, not a pattern.
Patterns have four elements. The argument seems to be that three of the four elements—name, context, forces to be resolved—belong to a “Concept,” and only the fourth—the one we don’t like—is a pattern.
Upon reflection, what I find most interesting about this perspective is how much it has in common with the pattern abuse that I have criticized in the past: an obsession with the surface form. I wrote about that above:
This line of thinking is grounded in the idea that the valuable part of the patterns is the suggested solution at a detailed level.
This criticism of patterns is rooted in exactly the same view of patterns: that he most important thing is the exact suggested solution, not the name, not the approach, not the context, not the forces to resolve. One person holds that up as a pattern’s value, another criticizes it, but they are much more similar than different in their views.