Design Patterns should be considered harmful.

Dave Cook
Compare the Market
Published in
6 min readAug 6, 2018

--

Ok, ok, I’ll level with you, the title of this post was deliberately inflammatory and intended to raise some eyebrows, but there is some truth in the claim.
I am actually an advocate of Design Patterns, however, in this post I will talk about issues I see with the way design patterns are typically demonstrated in .Net.

Background — Design Patterns and Gang Of Four
For those that don’t know, the book “Design Patterns: Elements of Reusable Object-Oriented Software” was originally published in 1994 by
Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides (the Gang of Four).
This was essentially a list of tried and tested solutions to common, reoccurring problems within the software world.

Armed with the knowledge that the book gave you, programmers suddenly had a toolbox at their disposal. Each tool has a specific purpose or problem that it could solve, just like a normal toolbox in the physical world: A saw is good at cutting, a hammer is good at driving in nails, and a screwdriver is good at tightening screws. And neither tool is particularly good at performing any of the tasks they were not designed to solve.
Knowledge of these patterns also gave programmers a shared language with which to quickly discuss implementation ideas and concepts,
without the need to dig deep into the details, and saved people time because you no longer needed to reinvent the wheel.

Design Patterns are part of a developer’s vocabulary

The book played an important part of the history of software development practices. Most of the experienced developers I have worked with over the years have read it, or some variant of it, and you will often see/hear big names in the industry reference things like “Factories” and “Builders”
There are countless sites on the web that provide code samples of how to implement the patterns in various languages.

I’m an advocate of Design Patterns
I spent years as a Software Engineering Manager encouraging my teams to read these materials and learn the patterns. I stand by those decisions!
However, since returning to a more hands-on role over the last year my opinion has changed.
I still believe in the usefulness of design patterns. And I would still encourage junior devs to learn the patterns and develop the ubiquitous language they give you.

But here’s my problem:
Almost all the code examples that explain the patterns are outdated compared to how experienced devs would implement them in the real world.

What do I mean by that?
Well, let’s take the Singleton pattern: essentially…there is only ever one instance of this object in existence at any one time.

A quick google and we find the common implementation of the pattern looks like this:

Taken from a post dated May 2018! 2018!!

Fairly simple stuff, but for those that don’t know: As there is no public constructor available for clients to call and create an instance of MyObject, in order to use the MyObject clients are forced to access the static property ‘Instance’, which ensures that there is only ever one MyObject in existence within your app at any one time.

So what’s my issue with this? Well, for a start it’s not entirely thread safe. (If you want a thread safe version, Jon Skeet has a post listing several different implementations of Singleton in a more thread safe way.)
But the real problem is that the code above completely ignores other advancements made within the .Net ecosystem since this ‘classic’ example was created.

Dependency Injection frameworks like Autofac, Ninject, and .Net Core’s built in DI are designed to manage object lifespan for you,
so you don’t need to worry about writing code to do this yourself.
You can simply declare MyObject as a singleton when you register it in your container, the code above can be removed, and your implementation of MyObject never needs to change, whether you want a single instance, one per request, or a new one every time its accessed.
It’s not the responsibility of the MyObject class to worry about its own lifetime.
Objects that are responsible for their own lifetime automatically have more than one responsibility, and therefore more than one reason to change — so they don’t conform with the Single Responsibility Principle.

So writing code for the Singleton pattern is basically redundant (Unless you are writing your own DI Framework!), and it’s a similar story for the Observer Pattern: Reactive Extensions for .Net (or Rx.net, a Microsoft maintained library, available on NuGet: search for system.reactive) provides implementations of the System.IObserver<T> and System.IObservable<T> interfaces.
So instead of writing code like this (https://www.dofactory.com/net/observer-design-pattern) you would simply implement the IObserver<T> interface on a class whose responsibility is to monitor a change in state of whatever is being observed, setup your class to subscribe to changes in that state, and perform actions based on a state change by implementing the OnNext(T) method.

Let’s look at another example, the Strategy Pattern: The basic idea behind the Strategy pattern is that clients should depend on a contract or abstraction rather than a specific implementation, and that various implementations of that contract can be swapped in and out, without any need to change your client code.

Again, a quick google for “Strategy Pattern .Net” and the typical example looks like this:

The classic Strategy Pattern example

What’s wrong with this then? In a word…Inheritance.
Classic OO teaching sells OOP on its ability to reuse code from a bass class and get what we want for free simply by inheriting from another class, and adding any additional behaviour you need. But what these classic teachings neglect to mention is that inheritance is a double-edged sword.
When you inherit from an object, you don’t just get the behaviours you wanted to reuse, you also change the public API of your own object as it now includes the behaviour of the parent object — some of which you may not actually want! This issue is magnified when the object you inherit from inherits from something else, and so on.

As a result of these issues, inheritance is rarely used by experienced .Net developers these days. Instead of inheriting from base classes (abstract or otherwise) we implement Interfaces instead.

If we refactor the example above to use an interface instead of an abstract class our example looks like this:

Strategy Pattern using an Interface rather than an inheritance

But you don’t get anything for free I hear you cry. No, no you don’t. And you don’t get anything you didn’t want or expect either!
You also don’t get any unwanted changes in behaviour in your child classes when you change the implementation details of a base class!
Besides, we weren’t getting anything for free with this example as the abstract class didn’t provide any implementation, it only specified implementation required by any children inheriting from it.

It’s the same issue with a lot of the other patterns: Chain of Responsibility, Decorator, Builder, Factory…all valid, valuable patterns that I use regularly, but the typical examples found on the web all use abstract bass classes rather than interfaces, and therefore teach new members of the .Net community the complete opposite of what they ought to be learning: “favour composition over inheritance”, which is a topic all of it’s own and beyond the scope of this post.

Summary:
I started off with a controversial statement about Design Patterns being harmful, which was a deliberate attempt to raise some eyebrows and get your attention.
I clarified that I am actually an advocate of Design Patterns and would recommend learning them to any developer who hasn’t already.
I explained that some design patterns have been made redundant by various developments in the .Net ecosystem, while others, as they are typically explained in common code examples,
are actually teaching bad practices compared to modern .Net development standards. I also touched upon a few alternative implementations, which I feel are more up-to-date with modern practices.

Finally, I’d like to amend my initial statement

What I actually mean is this:

Some Design Patterns, while valuable, should be considered harmful when followed to the letter of traditional implementation examples. Or to put it another way, if you are just starting out with Design Patterns, use extreme caution when it comes to following examples on-line.
I’m sure what I’ve written here will be out of date one day too!

--

--

Dave Cook
Compare the Market

Software Engineer, snowboarder, Archer & tired father...does not wear tights for any of these things! www.github.com/dcook-net