Singleton pitfalls: why the most famous design pattern should be used with caution. Pattern Singleton Design Pattern Using multiple interdependent singletons

09.02.2024 USB Flash Drives

Loner is a generative design pattern that ensures that a class has only one instance and provides a global access point to it.

Problem

A loner solves two problems at once, violating single responsibility principle class.

    Guarantees the existence of a single instance of a class. This is most often useful for accessing some shared resource, such as a database.

    Imagine that you created an object, and after a while you try to create another one. In this case, I would like to get the old object instead of creating a new one.

    This behavior cannot be achieved using a regular constructor, since the class constructor Always returns a new object.


Clients may not realize that they are working with the same object.

    Provides a global access point. This is not just a global variable through which you can access a specific object. Global variables are not write-protected, so any code can change their values ​​without your knowledge.

    But there is another nuance. It would be nice to store the code that solves problem No. 1 in one place, and also have a simple and accessible interface to it.

Interestingly, in our time the pattern has become so well known that people now call even those classes that solve only one of the problems listed above “singlers”.

Solution

All singleton implementations boil down to hiding the default constructor and creating a public static method that will control the life cycle of the singleton object.

If you have access to the singleton class, then you will have access to this static method. No matter where in the code you call it, it will always return the same object.

Analogy from life

The government of a state is a good example of a loner. A state can only have one official government. Regardless of who exactly sits in the government, it has a global access point “Government of Country N”.

Structure



    Loner defines a static method getInstance that returns a single instance of its class.

    The singleton constructor must be hidden from clients. Calling the getInstance method should be the only way to get an object of this class.

Pseudocode

In this example the role Singles plays the database connection class.

This class does not have a public constructor, so the only way to get its object is to call the getInstance method. This method will save the first object created and will return it on all subsequent calls.

// The singleton class defines a static method `getInstance` that // allows clients to reuse the same // database connection throughout the program. class Database is // The field for storing a single object must be declared // static.

private static field instance: Database // A singleton constructor should always remain private so that // clients cannot create // instances of this class themselves via the `new` operator.

When a program must have a single instance of some class that is available to all clients (for example, shared access to a database from different parts of the program).

The singleton hides from clients all methods of creating a new object, except for a special method. This method either creates an object or returns an existing object if one has already been created.

When you want to have more control over global variables.

Unlike global variables, Singletons ensure that no other code replaces the instantiated class instance, so you are always sure that there is only one singleton object.

However, at any time you can extend this limitation to allow any number of singleton objects by changing the code in one place (the getInstance method).

Implementation steps

    Add a private static field to the class that will contain a single object.

    Declare a static creation method that will be used to obtain the singleton.

The article will be useful primarily to developers who get lost during interviews when they hear the question “What are the main differences between a singleton and a static class, and when should one be used and when the other?” And it will certainly be useful for those developers who, when they hear the word “pattern,” become despondent or ask to stop expressing themselves :)

What is a static class?

First, let's remember what a static class is and why it is needed. Any CLI-compatible language uses the following global variable encapsulation paradigm: no global variables. All members, including static ones, can only be declared within a class, and the classes themselves can ( but they shouldn't) be grouped in some namespace. And while previously you had to imitate the behavior of a static class using a private constructor, the .NET Framework 2.0 added support for static classes at the platform level. The main difference between a static class and a regular, non-static one is that it is impossible to create an instance of this class using the operator new. Static classes are essentially a kind of namespace - only, unlike the latter, they are designed to house static variables and methods rather than types.

What is Singleton?

One of generating patterns first described by the Gang of Four (GoF). Ensures that the class has only one copy, and provides it with global access point. We will not consider this pattern in detail here, its purpose and the tasks it solves - there is a lot of detailed information about it on the Internet (for example and). I’ll just note that singletons can be thread-safe or not, with simple and lazy initialization.

And if there is no difference, why produce more?

So what exactly is the difference between these two entities and when should you use them? I think this is best illustrated in the following table:
Singleton
Static class
Number of access points
One (and only one) access point - static field Instance
N (depends on the number of public class members and methods)
Class inheritance
Possibly, but not always (more on this below)
Impossible - static classes cannot be instantiated because objects of static classes cannot be instantiated
Interface inheritance
Possibly without any restrictions

Possibility of passing as parameters
Possibly since Singleton provides real an object
Absent
Controlling the lifetime of an object
Perhaps - for example, lazy initialization(or creation on demand)
Impossible for the same reason class inheritance is impossible
Using an abstract factory to instantiate a class
Maybe
Impossible due to the lack of the ability to create an instance
Serialization
Maybe
Not applicable due to lack of copy

Let's take a closer look at the criteria listed above.
Number of access points
Of course we mean external access points, in other words, a public contract for the interaction of a class and its clients. It's easier to illustrate this with code:

Singleton in the "canonical" implementation:
public class Session ( private static Session _instance; // Pattern implementation... public static Session Instance ( get ( // ... return _instance; ) ) public IUser GetUser() ( // ... ) public bool IsSessionExpired() ( // ... ) public Guid SessionID ( get ( // ... ) ) )

Static class:
public static class Session ( // Access point 1 public static IUser GetUser() ( // ... ) // Access point 2 public static bool IsSessionExpired() ( // ... ) // ... // Access point N public static Guid SessionID ( get ( // ... ) ) )

Class inheritance
With inheritance of static classes, everything is simple - it is simply not supported at the language level. With Singleton things are a little more complicated. For ease of use, many developers most often use the following implementation of the pattern:
public class Singleton where T: class ( private static T _instance; protected Singleton() ( ) private static T CreateInstance() ( ConstructorInfo cInfo = typeof(T).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type, new ParameterModifier); return (T)cInfo.Invoke(null); ) public static T Instance ( get ( if (_instance == null) ( _instance = CreateInstance(); ) return _instance; ) ) public class Session: Singleton ( public IUser GetUser() ( // ... ) public bool IsSessionExpired() ( // ... ) public Guid SessionID ( get ( // ... ) ) )
And since multiple inheritance is prohibited in C# and in any CLI-compatible language, this means that we cannot inherit the Session class from any other useful class. The solution is to delegate access control to the object instance to the singleton:
public class Session: CoreObject ( private Session() ( ) public static Session Instance ( get ( return Singleton .Instance;
) ) )
Interface inheritance Using interfaces allows you to achieve greater flexibility, increase the amount of reusable code, increase testability, and, most importantly, avoid strong connectivity
objects. Static classes do not support inheritance at all. Singleton, on the contrary, fully supports interface inheritance, since it is a regular class. But this feature should be used only if the singleton instance is planned to be passed as input parameters in mixed scenarios or broadcast across domain boundaries. Mixed scenario example: // This class is a Singleton and implements the ISession interface public class Session: CoreObject, ISession ( private Session() ( ) public static Session Instance ( get ( return Singleton
.Instance;
) ) ) // This class is not a singleton and can generally be declared and implemented in another assembly // completely hiding the implementation details public class VpnSession: ISession ( ) public interface ISessionManager ( ISession GetSession(Guid sessionID); // Accepts the ISession interface, following the principles of reducing coupling bool IsSessionExpired(ISession session); Possibility of passing as parameters This is not supported for static classes - you can only pass the type, but in most situations this is useless, except when using reflection mechanisms (
// ... ISessionManager _sessionManager; // ... bool isExpired = _sessionManager.IsSessionExpired(Session.Instance);
Controlling the lifetime of an object
The lifetime of a static class is limited by the lifetime of the domain - if we created this domain manually, then we indirectly control the lifetime of all its static types. We can control the singleton's lifetime as we wish. A striking example is lazy initialization:
public class Singleton where T: class ( // ... public static T Instance ( get ( if (_instance == null) ( // Create "on demand" _instance = CreateInstance(); ) return _instance; ) ) )
You can also add an operation to remove a singleton instance:
public class Singleton where T: class ( // ... public static T Instance ( // ... ) // A very dangerous operation! public void RemoveInstance() ( _instance = null; ) )
This operation is extremely unsafe because the singleton may store some state and therefore its re-creation may have undesirable consequences for its clients. If, nevertheless, the need for such a method arose (which most likely indicates design errors), then you should try to minimize the possible harm from its use - for example, make it private and call it inside the Instance property under certain conditions:
public class Singleton where T: class ( // ... public static T Instance ( get ( if (!IsAlive) ( // Removal by condition RemoveInstance(); ) if (_instance == null) ( // Creation "on demand" _instance = CreateInstance(); ) return _instance; ) ) private void RemoveInstance() ( _instance = null; ) )
Using an abstract factory to instantiate a class
A static class does not support this feature due to the fact that an instance of a static class cannot be created. In the case of a singleton, everything looks simple:
public interface IAbstractFactory ( T Create (); bool IsSupported (); ) public class Singleton where T: class ( private static T _instance; private static IAbstractFactory _factory; protected Singleton(IAbstractFactory factory) ( _factory = factory; ) public static T Instance ( get ( if (_instance == null) ( _instance = _factory.Create ();
) return _instance;
public class Session: CoreObject, ISession ( private class SessionSingleton: Singleton ( protected SessionSingleton() : base(new ConcreteFactory2()) ( ) ) private Session() : base(new CoreContext()) ( ) public static Session Instance ( get ( return SessionSingleton.Instance; ) ) // ... )
Serialization
Serialization only applies to instances of classes. A static class cannot have instances, so there is nothing to serialize in this case.

So should you use Singleton or Static class?

In any case, the choice of solution depends on the developer and the specifics of the problem he is solving. But, in any case, the following conclusions can be drawn:

Using a singleton is justified when:

  • It is necessary to inherit classes or interfaces or delegate the construction of objects to a factory
  • Requires class instances
  • It is necessary to control the lifetime of an object (although this is a very rare task for a singleton)
  • It is necessary to serialize an object (this task is hypothetically possible, but it is difficult to imagine use cases)
Using static classes is advisable then when you don't need to implement any of the scenarios listed for the singleton. The main purpose of static classes is to group logically similar methods, constants, fields and properties. For example: System.Math, System.BitConverter, System.Buffer, System.Convert etc. Today I’ll tell you about the Singleton design pattern. Target: create a class that will have only ONE object. This means that no matter how many times it is accessed, the same object that was created the first time will be returned. This is a convenient thing and necessary in many places, it’s not for nothing that it is being implemented into frameworks. Application:
  • For example, you need to connect a database to a project and a class that will be responsible for connecting to it. The connection is created once and there is no need to create it again and again
  • Application settings - a class responsible for the settings required for the application: database host and port, etc. They are created once and used throughout the application's operation.
  • There are many more examples that I haven’t mentioned, so write your options in the comments! =)
After this introduction, as I understand it, you can show an example of this class: (Although I am sure that each of us can come up with an implementation of this) Here is the simplest example, when we make the constructor private, i.e. You cannot create an object explicitly. And there is a static method getInstance() that provides the object. public class Singleton ( private static Singleton instance; private Singleton () ( ) public static Singleton getInstance () ( if (instance == null) ( instance = new Singleton () ; ) return instance; ) ) There are problems with multithreading and then you can put the getInstance() method synchronized marker: public class Singleton ( private static Singleton instance; private Singleton () ( ) public static synchronized Singleton getInstance () ( if (instance == null) ( instance = new Singleton () ; ) return instance; ) ) In the end, as usual, I want to say that if you think differently or find a mistake in me - write in the comments! We will discuss everything with pleasure :) If you liked the article, write “+” and I will know it. This is important to me :) P.S. I’m adding more implementations: According to Joshua Bloch

The Singleton pattern is criticized by many, often called an anti-pattern. Nevertheless, it is quite popular and I have not yet come across large software systems where it would not be implemented. First of all, we can note frameworks where Singleton is often the basis of the application. It is also often inherited by components that interact with configuration data or the event mechanism, for example.

The main problem of this pattern is the captivating simplicity of its implementation and use. Developers who do not have enough experience use Singleton where it is completely unnecessary, thereby depriving the software system of flexibility and suitability for adequate testing.

In order to describe a design pattern, you first need to describe the problem that it will solve.

Let's say your application has an object that stores application configuration data. Of course, configuration data must be accessible to different parts of the application at all levels.

According to OOP ideology, to gain access to a variable, for example, inside a method of an object, it must be passed as a parameter to this method or constructor. You should try to follow this principle whenever possible. But, in the described situation, it is difficult to imagine how many objects an instance of the class for working with the config will have to be dragged through.
In PHP, there are two ways to place data in a global scope, thereby making it accessible from anywhere in the program. The first is to use a superglobal associative array $GLOBALS containing all the variables declared in the global scope. The second is to use the global keyword, which introduces a variable into the current scope. I find it difficult to even guess which method is worse. Be that as it may, other programming languages ​​do not have such tools and the Singleton pattern becomes the only way to introduce an object into the global space.

A class that implements the Singleton pattern becomes available globally through a static interface. Also among its features is the blocking of the class constructor and the magic methods __clone() and __wakeup(), describing them with the private access modifier. This is done to prevent the creation of more than one object from a class.

newPropetry = "string";

) public static function staticFoo() ( return self::getInstance(); ) private function __wakeup() ( ) private function __construct() ( ) private function __clone() ( ) ) Singleton::getInstance()->Foo() ; var_dump(Singleton::staticFoo()); ?>

The Singleton::$_instance class's only property stores a reference to an instance, which is created only the first time the static Singleton::getInstance() method is called. A conditional operator prevents the object from being re-created, checking the value of the property and if a reference to the instance already exists, it will be returned.

The Singleton::$_instance property is declared with the protected modifier so that the class can be inherited. Often a pattern is implemented by hiding this property behind the private modifier.

The implementation of the Singleton pattern can be seen, for example, in the CI_Controller class from the CodeIgniter framework or Zend_Controller_Front from ZendFramework.