The singleton (https://en.wikipedia.org/wiki/Singleton_pattern) is a well-known design pattern (if not the most), useful to guarantee the uniqueness of a resource (database, global system configuration…). Unfortunately, it is often misused by the simplicity that some developers find in accessing this resource via a class reference from anywhere in their code.
Consequently, this practice multiplies the direct dependencies to this class, and this at all levels of abstraction of the application.
This makes the code not scalable and links all implementations to your class method. A real problem if in the future you have to manage several databases, several icon sets….
A good practice is of course to apply the same methodology as to access any other instance and therefore to pass the singleton to the methods as a parameter, to store it in a configuration or any other solution that is normally applied. In short, solutions that a developer must already apply in his code.
In my opinion, this pattern makes the resulting bad practice too “tempting”, either because it is easy or because developers don’t know that it is a bad practice.
My solution (which is more of a reflection, actually) is that developers should be discouraged from doing this.
To this end, two objectives are pursued:
- inform the user that this is a bad practice
- make access to the singleton less easy
My implementation in Pharo (https://github.com/Larcheveque/Singleton) relies on exceptions to achieve these objectives.
The first one (SGLDirectSingletonAccessException) is raised when accessing the #uniqueInstance class method of your singleton. It informs you that this may be a bad practice.
This exception is however resumable, allowing you to catch it and resume it if you really want to access it. The interest is that the user clearly expresses the exceptional nature of accessing the singleton.
On another subject the common implementation of singletons generally prohibits you from creating a new instance (for example, by having a private constructor or by making an exception when calling #new). This can be a problem if you want to create another instance in a test context (it seems a bad idea to have the same database for tests as for production).
The same mechanism as above is applied, with an exception SGLSingletonInstanciationException launched by the #new of the singleton class. This exception is caught in the defensive strategy of the #uniqueInstance method which creates the singleton if it does not exist. So you can also catch it and summarize it in the #setUp of your test class in order to test your singleton.
In your execution, it is costly in terms of performance to launch exceptions and catch them in this way, it is rather a practice that we will try to avoid. However, in this case, direct access to the singleton is supposed to be reduced to a minimum (this is what we are aiming for), or even ideally to be unique. The cost is therefore negligible.
This implementation can be used with a Trait to transform any class into a singleton (SGLTSingleton).
In conclusion, this solution does not claim to be the ultimate answer to this problem, quite the contrary. It is rather a reflection on a rather ironic case of design pattern that leads to widespread bad practices, whether in open source or in industry.