One of the biggest lies we tell starting developers is that design patterns are language independent. Whilst true at a high level, the truth is that a programmer in a modern programming language can junk most of the Gang of Four book. A couple of days ago, it was twenty years old. It’s time to celebrate its lasting positive influences, and then bury it.
Some things are potentially useful as terminology for discussing with people, but others aren’t even useful as that. The really obvious example is the template pattern: if you’re programming in a language that can use functions as values it’s utterly meaningless. Another is iterator: most programming languages have a list/sequence implementation and you just use that.
Prototype, equally is meaningless for two, entirely opposite, reasons: first, the whole concept originates in C++ where you can perform a raw memory copy. In a language such as Java that doesn’t have one it’s so cumbersome you’ll prefer a factory method. In a language such as F# or Clojure, ubiquitous persistence data structures mean that everything’s a prototype.
Command is basically a pattern that replaces functions with objects. In a functional programming language, this is just the normal way you do things. In languages such as Python and Clojure where objects can act as functions the line is further blurred. But that’s nothing compared to what you can do with Clojure’s multimethods.
Multimethods and Protocols
Quite a few patterns are just workarounds for the painfully restricted dispatch patterns in old OO languages. The visitor and adapter patterns are both ways of circumventing the closed nature of classes in C++/Java. When you can just associate new methods with existing data structures, even third party code, you just don’t need them.
Also, if you understand multimethods for more than just class based dispatch, you see that it subsumes the state pattern.
(defmulti state-pattern (fn [tool data] tool)) (defmethod state-pattern pen-tool [tool data] nil)
How about a strategy pattern?
(defmulti strategy-pattern (fn determine-strategy [tool data] ...)) (defmethod state-pattern :strategy1 [tool data] nil)
In practice, you can use multimethods to mix and match dispatch on raw parameter value (state), dispatch on computed value (strategy) and dispatch on class (visitor). Similar effects can be achieved using Haskell’s type features.
Then there’s stuff that’s just a special case of something more general. Chain of responsibilty in Clojure is easily implemented using the
(defn chain-of-responsibility ([elements] (partial chain-of-responsibility elements)) ([elements data] (some #(% data) elements)))
Is chain of responsibility really useful terminology here, or is it just “using the
Then there’s ones that are just plain outdated: observer and mediator are rarely a better choice than a decent pub/sub mechanism. Heck, even your language’s event system is often a better choice. And I think everyone’s got the message about singleton by now.
I’m concerned this will be seen as down on the whole concept of patterns. Actually, high level patterns, the kind that Martin Fowler talks about are fine and last a long time. But our understanding of patterns constantly evolves (see pub/sub) and the ergonomics of specific patterns varies wildly between languages. GoF was a great book, and made a huge positive impact, but it’s time to take it off our shelves.