Since I heard of design patterns for the first time, there was one aspect of it that puzzled me. They seemed to be universally considered a good thing by itself. There seems to be a common belief that the lack of patterns in a program is a bad sign. Or, conversely, the ubiquitous presence of patterns in a software code is a good sign.
I never agreed to anything like that. Ideas like that go against my instincts. More specifically, the instinct to avoid recurrent code patterns (in the dictionary meaning). They are bad for maintenance, to mention one thing. Well, the so called design patterns are exactly that, recurrent code patterns. So, why are they are so popular? The reason is that their basic idea is mostly misinterpreted.
"Patterns" that are used recurringly in one language may be invisible or trivial in a different language.
And concludes affirming:
Patterns are signs of weakness in programming languages.
I agree with him. And so did most of the people that read and commented about the article. Ralph Johnson (one of the authors of the ”Design Patterns” book) wrote a reply. And Mark wrote a reply to Ralph’s reply. I think there are some ego sparks here and there, but they generally agree with each other. If you are surprised by their agreement, you definitely should read their texts.
Creational Patterns and Dependency Injection
Despite their value, I will not simply quote other people’s opinions. I have a point to make, too. I’ve been reviewing the whole subject recently, and an idea came to my mind. It’s not very innovative, but it’s worth phrasing anyway.
I’ll try to build my case around the singleton pattern. It’s possibly the simplest pattern, and the easiest one to understand. To begin with, I do not want to advocate it as bad pattern. For many simple situations, it fits the problem perfectly. I see it as an extreme simplified solution to a possibly more complex problem. It has drawbacks, though. Some cases require something similar, but just a little bit more sophisticated. I’ll go over some possible modifications to the singleton pattern through the rest of this article.
Before moving on, a comment. I’ll use some C# for my examples below for a single reason, it’s the language I know the most. I’m sure the general idea is valid for any language.
- Global State and Object Life Cycle
Everyone knows that global state is evil. The strongest argument against singletons is that they bring create global to an application. Depending on global state has consequences.
Suppose you have a singleton being used a multi-threaded application. A reasonable assumption is that the singleton object has to be thread-safe, i.e., it has to be able to be accessed by different threads without misbehaving. That’s not completely necessary.
One alternative is having a not-so-singleton object that has one instance per thread instead of one instance per process. This can be easily achieved with thread-local storage, which is supported by most programming languages. Even though these thread-local singletons are not valid to every problem, they are the easiest and safest way to have a thread-safe singleton.
We can go deeper on the idea of multi-instance-singletons (by the way, the term “singleton” might not be appropriate at this point, but that’s just a matter of naming). In many cases, what you really need is one instance per “context” or “call”, or whatever. A nice .NET example is on the
System.Web.HttpContext class. It has a static property called
Current that return an
HttpContext. This might not be commonly seen as an use of the singleton pattern, but it’s a very close variation. The way I see it, it’s basically a singleton that has one instance per http request.
Controlling the life cycle of “singleton” instances in these complex situations can be too complicated, and too specific to still be considered a pattern. That’s not actually a problem. I’ll get back to the subject later.
- Abstraction and Decoupling
Now, I want to expose a different facet of the pattern under discussion. Usually (at least in my code), the exposed type of the singleton instance is more abstract than its actual concrete type. Something like the C# code below.
public static readonly IAbstractThing Instance = new ConcreteThing();
You may reasonably argue that this is not a good usage of OO. If
IAbstractThing were destined to have only one concrete implementation, then you wouldn’t even need the interface to start with. But, in real world applications, that’s commonly not the case.
Exposing an abstract type opens some possibilities. You could, initially, delegate the responsibility of creating the instance to another component. By doing that, you achieve more than one advantage. You are now close to being able to create mock objects for unit testing. A secondary (or should I say primary?) advantage is better decoupling. The component that exposes the instance does not depend on any concrete implementations anymore, i.e., there will be one less dependency around
A group of components that work in this way (one component exposes an abstract object, and another creates the object) has dependency injection in it, even if no DI framework was used.
- Dependency Injection
Of course, the more sophisticated you get, the farther you get from the original pattern. All the intermediary variations are valid solutions for real problems. At each point, the decision of whether improve the code or not is a matter of balancing the costs of development (including test, support, etc.) with the actual advantages it will bring to the application.
The usual shortcut through such evolutionary process is a framework. And there is a lot of stable and dependable DI frameworks free for anyone to use. They are capable of providing a much more powerful solutions than any of the creational design patterns can, and at a lower cost. As I mentioned earlier, life cycle management might get very complex. Not with a good DI framework. Most of them support a broad range of extendable life cycle styles. By the way, my current favorite is Ninject. If you are a .NET programmer, I recommend that you give it a try.
If you are building a non-trivial application and find yourself using a big mix of abstract factories, builders, prototypes, or whatever, be sure of one thing. You are in need for a good DI framework. Any language that has built-in DI, or has a built-in DI library, or enables such a library to be build, do not need to depend on “creational patterns”.
A similar statement can be made to other types of design patterns. Consider the strategy pattern, for example. It’s trivially implemented using first-class functions (C#’s anonymous methods). The observer pattern can be effortlessly implemented with C# events. The iterator pattern can be graciously implemented with the
IEnumerable interface and some help from the
foreach keyword. Using the
yield return keyword, it’s possible to elegantly represent complex object interactions with a only a few lines of code.
Please, don’t get me wrong. Design patterns are good in many ways. For one thing, they provide a common vocabulary to programmers going through similar problems. What I’m saying is that they are generally misunderstood and overrated. Their ultimate purpose is to become invisible. Do not think design patterns are timeless. They aren’t.