Design Patterns: Elements of Reusable Object-Orientated Software was a book published in the early 90s that describes 23 classic software patterns. The book sold over 500,000 copies in 13 different languages. The four authors –– Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides –– even have a kitschy nickname in the software engineering world. People refer to them as the Gang of Four (GoF).
The most significant benefit of Design Patterns is that it introduced a universal language. The GoF didn’t discover or create the design patterns; most developers were already using them. They were simply the first people to curate, name and record them into a book. Before the book was released, every implementation was called something different, and people didn’t understand each other when they would talk about their implementations. Design Patterns gave everyone a shared understanding of what was what.
For fans of the book, the design patterns allowed engineers to work smarter and not harder; they were best-practice solutions that worked and could be reused over and over again. A dictionary definition of design patterns state they are “formalized best practices that the programmer can use to solve common problems when designing an application or system.” One thing expert designers know not to do is solve every problem. A designer who was familiar with the patterns could apply them to common problems without having to reinvent the wheel.
Design Patterns played an important part in the history of software development practices, but if you’re a developer, you already know this. Most software engineers read the book, or a variant of the book during their studies or after. Like anything this ubiquitous, it has drawn some criticism. Critics argue that some of the design patterns have been made redundant with new developments and that people use them unnecessarily. Others have argued that design patterns increase abstraction in your code. Patterns don’t protect you from bad design. What might start as simple –– turning to a tried and tested pattern –– becomes murkier, especially to your poor successor who will have a hard time figuring out what you’ve done.
Contentful customers often use design patterns when building their Contentful digital products. Contentful gives developers the freedom to implement any design pattern they choose. We have even provided a course in our learning center for content modeling design patterns. In the course, we go through assemblies, shared content, classification, navigation, and more.
While there is some criticism surrounding design patterns, the overwhelming feeling is positive, with one major exception: the singleton pattern. When the book first came out, designers flocked to the singleton design pattern because it appeared to be simple. It became widely used and abused early on, much to the annoyance of all the developers who then had to refactor it.
The singleton pattern is controversial. Some people go so far as to say singletons are evil. Others have likened singletons to sharks; they cruise along unnoticed under the surface, then pop up and bite you. We’re going to argue that it is an antipattern to be avoided at all costs. There are only a few legitimate uses for it –– we'll go over these, too.
There are three types of design patterns covered by the GoF: creational patterns, structural patterns, and behavioral patterns. The singleton pattern is creational. It deals with object creation mechanisms. The singleton pattern ensures that a class only has one instance and provides a global point of access to it. In other words, it restricts the instantiation of a class to one object. According to our Gang of Four, the singleton pattern offers controlled access to a sole instance, is more flexible than class operations, and permits refinement of operations and representation.
The biggest problem with the singleton pattern is that you assume there will only ever be one instance. This is a big assumption to make and often comes back to bite you. What you end up with is completely inflexible code.
For example, some people argue that entries such as a sitemap should be a singleton. You only want one instance of your sitemap and a single access point. But many instances of a sitemap can exist, potentially tagged by the released version of the site itself. On load, the site fetches the sitemap entry tagged by the current version of the site. Therefore, old users still get a valid sitemap for the version they are on. If singleton were used here, it would break that behavior. This inflexibility is one of the biggest reasons why singleton is an antipattern.
Engineers often use singletons to provide a global access point. They are often used in place of a global variable that has a single instance. But most developers steer clear from global variables, and singletons should provoke the same response.
When you have singletons in your code, it makes it hard to test and debug. Bugs might stay hidden even when you’re testing. It’s very difficult to write unit tests for code that uses singletons because it is generally tightly coupled with the singleton instance.
There is no way to mock out or inject a test instance of the singleton, which is terrible for maintenance, especially for the people who come after you. They will have the hard task of figuring out what’s going on under the surface.
There’s this idea that if you need just one of something, then the singleton pattern is your answer. This assumption isn’t true. Needing one of something doesn’t automatically mean you need the singleton pattern. A good standard when choosing to use a singleton or not is when having more than one instance is dangerous. You need a logistical explanation for locking up the object. If you examine your design more closely, you can almost always come up with a better design.
That said, there are still edge cases where using the singleton design pattern makes sense. Logging is a specific example of an "acceptable" singleton because it doesn't affect the execution of your code; your application does not behave any differently whether or not a given logger is enabled.
That leaves us with the question: Is the singleton design pattern an antipattern? We would have to say that it is. If you’re leaning toward using it, we would suggest thinking again. There is often a better solution that won’t leave behind a trail of confusion.