This article was automatically translated from the original Turkish version.
In the software world, certain structures are designed to be created only once. For example:
1. Using a single log file throughout an application,
2. Using a single database connection,
3. Ensuring that objects such as settings or configurations have only one instance.
Singleton Design Pattern is a software design pattern created out of necessity. It ensures that a class has only one instance and provides a global point of access to that instance.
The earliest Singleton implementations were very simple. The object is created when the application starts. When the object is needed later, the already created instance is returned.
Even if this object is never used, it occupies memory as soon as the application starts. For large resource-intensive objects, this can be a significant waste.
To solve this problem, the approach “Create only when needed” was developed:
This structure can cause errors in a multithreaded environment. If two different threads call the getInstance() method simultaneously, two separate instances may be created because both threads can check that the instance is null and proceed to create one before the other has finished. This breaks the Singleton principle.
This issue can be resolved using a synchronization mechanism like a mutex.
Yes, it is thread-safe, but it locks on every call. This affects performance. Once a single instance has been created, there is no need to lock again, yet here locking occurs every time.
To avoid performance loss, the double-checked locking pattern was introduced:
This structure ensures that:
Even this approach did not produce the desired result when first introduced, due to the structure of programming languages at the time. The issue stemmed from older language implementations. To achieve the intended behavior, the volatile keyword was introduced. The detailed reasons for this are beyond the scope of this article.
Once widely favored, the Singleton is now regarded as an anti-pattern in most scenarios. Here are the reasons:
1. It introduces global state, making testing difficult.
2. It hides dependencies, contradicting dependency injection principles.
3. It becomes hard to control its impact elsewhere in the code (e.g., a setting silently changed).
4. Genuine cases requiring a truly single instance are very rare.
5. It causes complications in parallel programming.
6. It adds unnecessary complexity.
The Singleton was born to solve a small problem but over time began causing larger ones. In modern software development:
Eager Singleton
What Is the Problem?
Lazy Singleton (Lazy but Smart)
What Is the Problem With This?
Mutex (Synchronized Singleton)
But This Is Also Problematic…
Double-Checked Locking Pattern
So Why Is Singleton Now Considered an Anti-Pattern?
In Summary