Allowed global variables and supposed memory savings For 20 years I have been teaching software at the University of Buenos Aires. In the software engineering course we teach design patterns and the same “scheme” is always repeated as a repeating deja vu, the same sequence that I had the opportunity to witness in several of my works and in the free software that I use: the ‘magical’ appearance of the Singleton pattern. Programmers love Singleton pattern Hopefully, after reading this article, we start using it a little bit less. The origin of evil The pattern has been used in the industry for decades. Its popularity is attributed to the excellent book . There are numerous software frameworks that use it and we rarely find literature that discourages its use. Design Patterns Despite this, in the corresponding entry we can read a Dantesque warning: Wikipedia Critics consider the singleton to be an in that it is frequently used in scenarios where it is not beneficial, introduces unnecessary restrictions in situations where a sole instance of a class is not actually required, and introduces into an application. anti-pattern global state Photo by Alessio Zaccaria on Unsplash Let's be pragmatic as always, and look at the arguments for and against its use: Reasons not to use it e 1. Violates th Bijection Principle As we saw in previous articles, every object in our computable model has to be on a relationship with a real-world entity. mapped 1 to 1 Singletons are often linked to objects that need to be unique. As usual we will have to distinguish among the objects that are unique (for problem domain drivers) and differentiate them from the unique ones regarding implementation reasons, efficiency, resource consumption, global access, etc. essentially accidentally Most unique objects are not present in the real world, and we will see later on that the presumably unique ones may not be so if we consider different contexts, environments, or situations. accidentally essentially The One and Only Software Design Principle 2. Generates coupling It is a global reference. Again according to : Wikipedia An implementation of the singleton pattern must provide to that instance. global access What a priori appears as a benefit for preventing us from having to pass context information, generates coupling. The reference to the singleton cannot be changed according to the environment (development, production), nor can dynamic strategy changes related to the current load be made, it cannot be replaced by a double test and it prevents us from making changes due to the possible ripple effect. Coupling: The One and Only Software Designing Problem 3. It says a lot about (accidental) implementation and little about his (essential) responsibilities By focusing early on implementation issues (the is an implementation pattern) we orient ourselves according to ( ) and underestimate the most important thing of an object: the responsibilities it has (what). Singleton accidentality how When carrying out premature optimization in our designs, we usually award a concept that we have just discovered as singleton. 4. It prevents us from writing good unit tests The aforementioned coupling has as a corollary; the impossibility of having full control over the side effects of a test to guarantee its determinism. We must depend on the global state referenced by the . Singleton 5. Does not save memory space The argument used to propose its use is to avoid the construction of multiple volatile objects. This supposed advantage is not real in virtual machines with efficient garbage collection mechanisms. In such virtual machines, used by most modern languages, keeping objects in a memory area whose algorithm is double pass ( ) is much more expensive than creating volatile objects and quickly removing them. Garbage Collector mark & sweep 6. It prevents us from using dependency injection As good solid design advocates, we favor inversion of control through dependency injection to avoid coupling. In this way the service provider (formerly a hardcoded Singleton) is decoupled from the service itself, replacing it with an injectable dependency that meets the defined requirements, coupling us to and not . what how 7. It violates the instantiation contract When we ask a class to create a new instance we expect the contract to be honored and give us a fresh instance. However, many Singleton implementations hide the creation omission , rather than failing quickly to indicate that there is a business rule that instances should not be arbitrarily created. new silently Fail Fast Philosophy, Explained A better answer would be to show with an exception it is not valid to create new instances in this execution context. This will force us to have a private constructor to use it internally. Thus violating the contract that all classes can create instances. Another . code smell 8. It forces us to explicitly couple to implementation When invoking a class to use it (again, to use its ), we will have to couple with the fact that it is a Singleton (its ), generating a relation that, when trying to break it, would produce the much-feared ripple effect. what accidentally how 9. It hinders the creation of automated tests If we use the TDD technique, objects are defined purely and exclusively based on their behavior. Therefore, in no case, the construction of software using TDD will arise the concept. If business rules state that there must be a single provider of a certain service, this will be modeled through a controlled access point (which should not be a global class, much less a ). development Singleton Singleton Trying to create unit tests in an existing system coupled to a can be an almost impossible task. Singleton 10. Unique concepts are contextual When the pattern is stated it is usually accompanied by some idea that in the real world seems rather unique. For example, if we want to model the behavior of according to the vision of Christianity, there could not be more than one . But these rules are relative to the context and subjective vision of each religion. Various belief systems may coexist in the same world with their own gods (some monotheistic and other polytheistic beliefs). God God Pattern structure according to the design pattern book The class (and all the metamodel) is not present in the bijection. Any relationship linked to the class will be invalid 11. It is difficult to keep up in multi-threaded environments Pattern implementation can be tricky in programs with multiple threads. If two try to create the instance at the same time and it does not exist yet, only one of them should succeed in creating the object. The classic solution to this problem is to use in the class creation method that implements the pattern, to make sure it is reentrant. execution threads mutual exclusion 12. Accumulates garbage that takes up memory space Singletons are references attached to classes, just as classes are global references these are not reached by the garbage collector. In case the is a complex object, this entire object, in addition to the transitive closure of all its references, will stay in memory throughout the execution. Singleton 13. The accumulated garbage state is the enemy of unit tests The persistent state is the enemy of unit tests. One of the things that makes unit tests effective is that each test must be of all the others. If this is not true, then the order in which the tests are run may affect the test results and the tests become . This can lead to cases where tests fail when they shouldn’t, and worse, can lead to tests that pass only in the order they were performed. This can hide mistakes and is very bad. independent non-deterministic Avoiding variables is a good way to prevent the state from being preserved between tests. , by their very nature, depend on an instance that is kept in a variable. This is an invitation for the dependency test. static Singletons static Photo by Brian Yurasits on Unsplash 14. Limiting the creation of new objects violates the single responsibility principle. The single responsibility of a class is to create instances Adding any other responsibility to any class implies violating the (the S for Solid). A class should not worry about being or not being a . They should only be responsible for their commitments to business rules. In case of needing the uniqueness of these instances this would be the responsibility of a third object in the middle such as a Factory or a Builder. single responsibility principle Singleton 15. The cost of having a global reference is not just the coupling Singletons are frequently used to provide a global access point to some service. What ends up happening is design dependencies are hidden within the code and are not visible when examining the interfaces of their classes and methods. The need to create something global to avoid passing it explicitly is a . There are always better solutions and alternatives to using a global reference, that do not require passing all collaborators between methods. code smell 16. He’s the easy friend from the party Many singletons are themselves abused as a global reference repository. The temptation to use the singleton as an entry point for new references is Huge. There are many examples where a is used as a quick-reach reference container. Singleton As if it was not enough to be the root of all evil he is also the easy friend of the party. In large projects, it just accumulates garbage to get out of trouble. Since it does not have a corresponding entity in the bijection, adding responsibilities, that do not correspond to it, is like adding one more stain to the tiger. Apparently without doing damage but generating ripple effect when wishing to do a healthy decoupling. Photo by Omar Lopez on Unsplash 17. Evidence shows more errors on software using Singletons There are several Root Cause Analysis and post mortems on production defects correlated to systems and the lack of testing it hinders. The Singleton Design Pattern: Impact Quantified Reasons to use it Having stated the arguments against Singleton let’s try to see the possible benefits: 1. It allows us to save up memory This argument is fallacious according to the current state of the art of languages with a decent virtual machine and garbage collector. It is enough to carry out a benchmark and look for evidence to convince us. 2. It’s good for unique concepts modelling The Singleton can be used to guarantee the uniqueness of a concept. But it is not the only way or the best. Let’s rewrite the previous example: Access and creation of the single instance are not coupled. Creation unicity is done in a single controlled point and direct references to classes are decoupled. 3. It prevents us from repeating expensive initializations There are objects that require a certain cost of resources to create. If this cost is large, we will not be able to generate them constantly. One possible solution is to use a and have it available all time. Singleton As always we will focus on the and we will look for some other generating less coupling. If we need a single control point or a we will have to access a known object related to a certain context (and easily replaceable according to the environment, the test setup, etc.). Certainly a will not be our first choice. what hows cache Singleton Solutions There are multiple techniques to gradually remove the (ab)use of Singletons. In this article we show some: How to Decouple a Legacy System Conclusions The disadvantages listed in this article are much greater than the advantages, and the evidence from the examples in the industry should be a strong indicator for the in any case. non-use of the evil pattern As our profession matures, we will leave behind these kinds of bad solutions. Part of the objective of this series of articles is to generate spaces for debate and discussion on software design. We look forward to comments and suggestions on this article.
Share Your Thoughts