Why singleton breaks open/closed principle?
It is a common misconception that the Singleton design pattern prohibits inheritance. That is simply not true.
From the GoF book, page 127:
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.
...and again on page 128:
The Singleton pattern has several benefits:
- Permits refinement of operations and representation. The Singleton class may be subclassed, and it's easy to configure an application with an instance of this extended class. You can configure the application with an instance of the class you need at run-time.
Page 130 goes into detail about configuring Singleton subclasses.
The answer to why Singleton breaks the OCP is that it doesn't, at least not inherently. It just happens to be most commonly implemented in violation of the OCP by developers who have not read the GoF book. The Factory Method pattern suffers much the same fate.
For a class to be "open" it must be possible to inherit from it. Inheritance is an "is-a" relationship. If you inherit from a singleton-class then instances of the child-class are also instances of the parent class due to the "is-a" relationship, meaning you can suddenly have multiple instances of the singleton class.
If the singleton class inhibits inheritance, it's no longer "open".
If a singleton class allows inheritance, and is "open" for extension, then it can no longer enforce the singleton pattern.
There are two problems with the Singleton pattern:
- It breaks the Open/Closed Principle, because the singleton class itself is in control over the creation of its instance, while consumers will typically have a hard dependency on its concrete instance. This disallows the implementation to be changed, without having to make sweeping changes throughout the application.
- It breaks the Dependency Inversion Principle (DIP), because consumers will always depend directly on the concrete class to get the instance, while the DIP prescribes the use of abstractions. This causes the Singleton implementation to be dragged along and disallows adding cross-cutting concerns by wrapping the implementation in a decorator or distributing the client without that singleton implementation.