docs: update singleton

This commit is contained in:
Ilkka Seppälä
2024-05-20 20:58:05 +03:00
parent e76bdffab4
commit 547cf20140
6 changed files with 47 additions and 30 deletions
+38 -22
View File
@@ -3,9 +3,15 @@ title: Singleton
category: Creational
language: en
tag:
- Gang of Four
- Gang of Four
- Instantiation
- Resource management
---
## Also known as
* Single Instance
## Intent
Ensure a class only has one instance, and provide a global point of access to it.
@@ -14,8 +20,7 @@ Ensure a class only has one instance, and provide a global point of access to it
Real-world example
> There can only be one ivory tower where the wizards study their magic. The same enchanted ivory
> tower is always used by the wizards. The ivory tower here is a singleton.
> A real-world analogy for the Singleton pattern is a government issuing a passport. In a country, each citizen can only be issued one valid passport at a time. The passport office ensures that no duplicate passports are issued to the same person. Whenever a citizen needs to travel, they must use this single passport, which serves as the unique, globally recognized identifier for their travel credentials. This controlled access and unique instance management in the real world mirrors how the Singleton pattern controls the instantiation of a class in software.
In plain words
@@ -23,9 +28,7 @@ In plain words
Wikipedia says
> In software engineering, the singleton pattern is a software design pattern that restricts the
> instantiation of a class to one object. This is useful when exactly one object is needed to
> coordinate actions across the system.
> In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system.
**Programmatic Example**
@@ -57,7 +60,7 @@ enumIvoryTower2=com.iluwatar.singleton.EnumIvoryTower@1221555852
## Class diagram
![alt text](./etc/singleton.urm.png "Singleton pattern class diagram")
![Singleton](./etc/singleton.urm.png "Singleton pattern class diagram")
## Applicability
@@ -66,29 +69,42 @@ Use the Singleton pattern when
* There must be exactly one instance of a class, and it must be accessible to clients from a well-known access point
* When the sole instance should be extensible by subclassing, and clients should be able to use an extended instance without modifying their code
Some typical use cases for the Singleton
* The logging class
* Managing a connection to a database
* File manager
## Known uses
* The logging class
* Configuration classes in many applications
* Connection pools
* File manager
* [java.lang.Runtime#getRuntime()](http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#getRuntime%28%29)
* [java.awt.Desktop#getDesktop()](http://docs.oracle.com/javase/8/docs/api/java/awt/Desktop.html#getDesktop--)
* [java.lang.System#getSecurityManager()](http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager--)
## Consequences
* Violates Single Responsibility Principle (SRP) by controlling their creation and lifecycle.
* Encourages using a globally shared instance which prevents an object and resources used by this object from being deallocated.
* Creates tightly coupled code. The clients of the Singleton become difficult to test.
* Makes it almost impossible to subclass a Singleton.
Benefits:
* Controlled access to the single instance.
* Reduced namespace pollution.
* Allows refinement of operations and representation.
* Permits a variable number of instances (more than one, if desired).
* More flexible than class operations.
Trade-offs:
* Difficult to test due to global state.
* Potentially more complex lifecycle management.
* Can introduce bottlenecks if used in a concurrent context without careful synchronization.
## Related Patterns
* [Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/): Often used to ensure a class only has one instance.
* [Factory Method](https://java-design-patterns.com/patterns/factory-method/): Singleton pattern can be implemented using a Factory Method to encapsulate the creation logic.
* [Prototype](https://java-design-patterns.com/patterns/prototype/): Avoids the need to create instances, can work alongside Singleton to manage unique instances.
## Credits
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
* [Effective Java](https://www.amazon.com/gp/product/0134685997/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0134685997&linkCode=as2&tag=javadesignpat-20&linkId=4e349f4b3ff8c50123f8147c828e53eb)
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
* [Effective Java](https://amzn.to/4cGk2Jz)
* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3yhh525)
* [Refactoring to Patterns](https://amzn.to/3VOO4F5)
@@ -89,7 +89,7 @@ public class App {
LOGGER.info("enumIvoryTower1={}", enumIvoryTower1);
LOGGER.info("enumIvoryTower2={}", enumIvoryTower2);
// double checked locking
// double-checked locking
var dcl1 = ThreadSafeDoubleCheckLocking.getInstance();
LOGGER.info(dcl1.toString());
var dcl2 = ThreadSafeDoubleCheckLocking.getInstance();
@@ -45,7 +45,7 @@ public final class BillPughImplementation {
}
/**
* The InstanceHolder is a static inner class and it holds the Singleton instance.
* The InstanceHolder is a static inner class, and it holds the Singleton instance.
* It is not loaded into memory until the getInstance() method is called.
*/
private static class InstanceHolder {
@@ -62,7 +62,7 @@ public final class ThreadSafeDoubleCheckLocking {
// Check if singleton instance is initialized.
// If it is initialized then we can return the instance.
if (result == null) {
// It is not initialized but we cannot be sure because some other thread might have
// It is not initialized, but we cannot be sure because some other thread might have
// initialized it in the meanwhile.
// So to make sure we need to lock on an object to get mutual exclusion.
synchronized (ThreadSafeDoubleCheckLocking.class) {
@@ -72,7 +72,7 @@ public final class ThreadSafeDoubleCheckLocking {
// just like the previous null check.
result = instance;
if (result == null) {
// The instance is still not initialized so we can safely
// The instance is still not initialized, so we can safely
// (no other thread can enter this zone)
// create an instance and make it our singleton instance.
result = new ThreadSafeDoubleCheckLocking();
@@ -85,7 +85,7 @@ abstract class SingletonTest<S> {
* Test singleton instance in a concurrent setting.
*/
@Test
void testMultipleCallsReturnTheSameObjectInDifferentThreads() throws Exception {
void testMultipleCallsReturnTheSameObjectInDifferentThreads() {
assertTimeout(ofMillis(10000), () -> {
// Create 10000 tasks and inside each callable instantiate the singleton class
final var tasks = IntStream.range(0, 10000)
@@ -96,7 +96,7 @@ abstract class SingletonTest<S> {
final var executorService = Executors.newFixedThreadPool(8);
final var results = executorService.invokeAll(tasks);
// wait for all of the threads to complete
// wait for all the threads to complete
final var expectedInstance = this.singletonInstanceMethod.get();
for (var res : results) {
final var instance = res.get();