Are design patterns compatible with modern software techniques?

! Warning: this post hasn't been updated in over three years and so may contain out of date information.

Design patterns: who really uses them? Are they just ways of overcoming limitations in Java? Are they an idea that has had its time? Do they really aid understanding of ideas between programmers, or do they just cause more confusion? Or, are they a great, but often misunderstood, idea that is still relevant today and that can be applied to all languages? After much thought and discussion with unsuspecting folk who have suffered a string of daft questions from me, I finally feel able write down my take on software design patterns and my conclusions might surprise many that know me.

I have been struggling with software design patterns for a number of years now. To explain why, I need to share a quick history of my relationship with them. When I first encountered the term around six years ago, having a set of identified patterns seemed a great idea to me. Various ways I’d been writing code suddenly had names: Singleton, Service Locator, Factory etc. Other ideas I’d not encountered before also had names: Dependency Injection, Inversion of Control. These new terms, and the ideas behind them, interested me greatly and I started to investigate them in detail.

Within that new knowledge lay the downfall of design patterns for me though, even as I was still learning about them. At the same time as I started to encounter design patterns, so I also encountered the world of test-driven development (TDD). Through the use of TDD, I was then exposed to the likes of clean code, design to interfaces and tell-don’t-ask principles. One thing that using these techniques taught me was that certain ways of coding were detrimental to ease of testing. Unfortunately, the Singleton and Service Locator patterns were powerful examples of this way of programming. When one gets right down to it, these patterns are just glorified global variables. They are code smells, or anti-patterns if you prefer. Add to this an intense dislike I had for the over-complex UML and code examples that seemed to be associated with every pattern I encountered, and my enthusiasm started to wane rapidly. Further, much that I read about the Model, View, Controller (MVC) pattern (which I’d started using heavily within a Flex solution I was involved with) argued that it wasn’t really a pattern as it was too large. It was instead a framework. Lastly there was the observer pattern. What was this monstrosity with its vast collection of classes and interfaces? Hadn’t the authors of descriptions of this pattern ever heard of events?

Each of the design patterns I looked at seemed to be an example of over-engineering a solution to really quite simple problems. Take for example the command pattern. If one looks at Wikipedia for details of the this pattern, one is informed that:

  1. The command pattern is an OO design pattern “in which an object is used to represent and encapsulate all the information needed to call a method at a later time”.
  2. “Four terms always associated with the command pattern are command, receiver, invoker and client.” (Bold emphasis mine).

The author(s) then present detailed descriptions of these four terms and a complex UML diagram to proscribe its implementation. Finally, we get to an example of using the pattern in C#:

The above code smells worse than untreated sewage and anyone in my team writing such bad code would be selected for immediate (re)training. The following is how I’d expect the above functionality to be written:

The second code example is far simpler and so is both less prone to bugs and is more readable. If one reads about design patterns in most literature, one is struck by how proscriptive the offered solutions are, how over-complex they are and by how classic, class-based, OO-centric they are. It was because the literature for the standard “gang of four” (GoF) patterns was all flawed like this that my enthusiasm for patterns died and I finally gave up on them.

During the subsequent years, I have occasionally bumped into design patterns. Sometimes it’s through people telling me I should learn them, often for no other reason than questions about them are often asked in interviews. That has always seemed a pretty poor reason for learning about something to me. I’m not sure I’d want to work for any organisation that puts so much weight on something I have had so little time for. Other times I’ve encountered them through people championing the idea that design patterns are an irrelevance to functional programming. They actually seemed to be nothing more than ways of coping with shortcomings of many OO languages. This resonated well with me and – until a couple of months ago – I’ve adopted a rather smug “I’m too good for design patterns” attitude.

Recently I’ve been mentoring one of our testers who wants to move across into development. He had previously had some experience of Java, but I wanted him to become involved in a C# project that we were working on. I was also keen to teach him TDD and pair programming techniques. Rather arrogantly though, I assumed this would be a completely one-way process of me sharing my knowledge with him. Needlessly to say, I was soon put in my place. Amongst many things that he caught me out with occurred when I introduced him to the idea of using Dictionary<string, Func<T>> to encapsulate a set of similar methods that would be chosen by a string value (as used in the above code example). This is a pretty basic programming idea for someone like me who “grew up” on C function pointers. What surprised me was being informed by our trainee developer that this was an example of the command pattern!

The trainee had opened my eyes to another way of looking at design patterns: the likes of the Wikipedia article and the GoF book are simply badly written. They use absolute terms, rather than suggesting what they are presenting is just one way of expressing a particular pattern. If the command pattern article started with something like “The command pattern encapsulates all the information needed to call a method or function at a later time”, then it would be less specific to class-based languages and might encourage less scorn. If the code examples made full use of each language’s features to minimise complexity, they might even be useful. Perhaps there was hope for design patterns after all.

The authors of the GoF book presumably aren’t stupid people: there must be a reason they made their patterns so proscriptive and complex. I don’t know for sure, but I suspect that they were deliberately aiming for a set of language-agnostic patterns. They appear to have been written in a way to actively avoid making use of language-specific features. Take the observer pattern for example. In recent years, I’ve worked with ActionScript and C#. Both of these languages natively support events. These languages have both appeared since the GoF book was written. The observer pattern is written in such a way that it supports event-like behaviour in all OO languages in a consistent fashion. One can take C++ or Java code written with that pattern and port it to C# or ActionScript with ease. The same doesn’t apply when taking event-based code written in C# that needs porting to SmallTalk for example.

I think the idea of language-agnostic patterns is itself an anti-pattern though. By avoiding language-specific features, the code ends up more complex than it need be. In this age of TDD, I’d not expect to have to port code from one language to another. I’d expect to port the tests and write language-specific code to make those tests pass. The way many patterns are described has had its day. However as that young trainee had pointed out to this arrogant veteran, just because the implementations need retiring, doesn’t mean the patterns themselves are no longer needed. One of the original aims of patterns was to help improve communication between developers. When teaching a Java developer about C#’s delegates, perhaps we should simply say “this is how we implement the command pattern in C#”. If we both understand the command pattern to be a way to “encapsulate all the information needed to call a method or function at a later time”, then explaining delegates becomes so much easier.

By viewing patterns as ideas, which can be implemented in very different ways in different languages, we also bring the functional language fans on board. As Peter Norvig puts it in Design Patterns in Dynamic Languages from 1996, “16 of 23 patterns have qualitatively simpler implementation in Lisp … than in C++”. This has been interpreted by many to mean “16 of the 23 GoF patterns aren’t needed for functional programming”, but that interpretation doesn’t apply if patterns are seen as ways of expressing ideas, rather than proscribing solutions. In fact the idea can then be taken further. The simplest answer to the question “what is a monad?” is “it’s a design pattern for expressing asynchronous state”. Whilst far from a complete answer, it removes the need to immediately talk of functional languages. After all, as Mike Hadlow argues on his blog “IEnumerable is in fact a Monad”, ie monads exist in the OO world too.

As design patterns are currently normally presented, I see them destined to be consigned to the bad-code dustbin of history. Some, like the singleton pattern, describe bad practice and so should be dumped for sure. However, a young lad by the name of Hao has helped me see that many are still relevant to all modern languages. With some work expressing them in more useful, generic terms, there’s no reason why they shouldn’t have a future.

5 thoughts on “Are design patterns compatible with modern software techniques?

  1. Did you ever actually read the GoF book, it’s old admittedly but it does try to get the ideas of relevant ways to solve common problems without tying to any particular language. Singleton has been misused but it does still have relevant uses today for example where there must only be one not simply where it’s easier to have one, otherwise the clever people that come up with the JEE spec wouldn’t have defined the @Singleton EJB. As for mvc being a framework not a pattern, the framework is the implementation of a pattern and I’ve seen the crappy implementation from flex and can see why you’d be disheartened by it. Glad to see you can still evolve as a developer keep it up šŸ™‚

  2. “Iā€™m not sure Iā€™d want to work for any organisation that puts so much weight on something I have had so little time for.”

    … made my day. What?! šŸ™‚

  3. I just had a nice conversation with you at StackOverflow on IoC and after reading your post, I think I can see your point. In your world, method-level contract is a more clean way of expressing mutual obligations between different parts of code. This clearly corresponds to your “functional” background. And yes, often this leads to a cleaner and easier code, as your Command example shows. However, in contrast to functions, objects do have STATE. A string, a date, a complex object. Now, think of your “agile” command implementation but let each command have additional parameters and also, let it execute asynchronously, where each command is persisted somewhere and retrieved and then executed somewhere else (over the wire perhaps?). When command=object, what you have to do is to just serialize/deserialize your command object, it preserves the state. When command=function, there is no easy way to serialize/deserialize it to include all possible captured “parameters”. This particular example shows that design patterns are ideas to organize responsibilities of “beings” which participate in a complex task but the actual way of implementing these “beings” could possibly vary – some high-level concepts (events, closures) of specific languages can possibly make the implementation cleaner but not always. On the other hand, the class-level contract is a common denominator of all these possible complex tasks and thus patterns are expressed in at object-oriented level. On the other hand, there are notions you could possibly call “functional patterns”, like monads, which don’t have clear OO counterparts and still, this doesn’t make monads not interesting just because some monads could have an easier implementation in an imperative language. During my patterns lecture, I always encourage students to UNDERSTAND the idea behind each single pattern and to judge whether or not the idea is worthy but never, ever stick to particular implementation. I really liked your conclusion then. Regards.

  4. If you separate the problem and the solution, then Design patterns are solutions to common problems that are not already solved by the language. Adding first class functions to a language makes whole categories of design patterns go away. The problem is still there, but you no longer need a pattern or something similar to solve it…just use the language feature.

    Design patterns are fundamental to specific implementations of specific languages. They are not fundamental to programming.

    This becomes clear when using functional programming techniques. You may still use common patterns, but they will be different than the OO patterns and will vary with language.

Comments are closed.