Singleton pattern. Usage when single classes form an inheritance hierarchy. C++, C#, Java implementations
Before studying this topic, it is recommended that you familiarize yourself with the following topic:
Contents
- 1. Generalized structure of the Singleton pattern. Defining the problem
- 2. Spawning and using a subclass in the Singleton pattern. Example implementation in C++
- 3. An example of spawning and using a subclass in the Singleton pattern. Java implementation
- 4. An example of using the Singleton pattern for 3 classes that form a hierarchy. C# implementation
- Related topics
Search other websites:
1. Generalized structure of the Singleton pattern. Defining the problem
Before using the example, it is advisable to consider the simplified structure of the Singleton pattern (Figure 1).
Figure 1. Simplified structure of the Singleton pattern
In the Singleton pattern, subclasses can be inherited from a Singleton class that concretize (extend) the operation of the methods (operations) of the base class. A task arises that should provide answers to the following questions:
- how to define an inherited class;
- how to make clients use a single instance of an inherited class. Here you need to take into account that several subclasses can be inherited from the base class.
⇑
2. Spawning and using a subclass in the Singleton pattern. Example implementation in C++
2.1. Class diagram forming a hierarchy
The example demonstrates the use of the Singleton pattern for three classes A, B, C, which form the inheritance hierarchy shown in Figure 2.
According to the condition of the problem, you need to control the creation of no more than one instance of each class. That is, the client can create a maximum of one instance of each of the classes (3 instances in total).
The simplest way to solve the problem is to add its own static instance and static method Instance() to each class.
Figure 2. The scheme for solving the problem. Class hierarchy A, B, C. Static methods Instance() in classes
As you can see in Figure 2, each of the classes has its own Instance() method, which creates a single instance of this class. Each class declares a reference (pointer) to the only created instance.
⇑
2.2. Source code in C++ language
#include <iostream> using namespace std; // Implementation of the Singleton pattern in C++. // Demonstration of using the Singleton pattern // in the case when classes form an inheritance hierarchy. // Task. Control the creation of no more than one instance of each class, // which form an inheritance hierarchy. class A { private: // A static internal variable that stores an instance of class A. static A* _instance; // Some class data, there can be any data here int a; protected: // Constructor A() { a = 0; } public: // A static method that returns an instance of class A. static A* Instance() { if (_instance == nullptr) { // Create the instance _instance = (A*) new A(); return _instance; } else { return nullptr; } } // Methods (operations) to access internal class data int Get() { return a; } void Set(int _a) { a = _a; } // Method that outputs the value of the internal field d void Print() { cout << "A.a = " << a << endl; } // Class destructor - correct memory release ~A() { delete[] _instance; } }; // Class B - inherits from class A class B : public A { private: static B* _instance; // Some internal data of class B, here you can put your own data int b; protected: // Protected constructor of class B B() { b = 0; } public: // A static method that returns an instance of class B static B* Instance() { if (_instance == nullptr) { // Create the instance _instance = (B*) new B(); return _instance; } else { return nullptr; } } // Internal data access methods (variable b) int Get() { return b; } void Set(int _b) { b = _b; } // Print the value of the internal variable b for control void Print() { cout << "B.b = " << b << endl; } // Class destructor - correct memory release ~B() { delete _instance; } }; // Class C - inherits from class B class C :B { private: // The instance of class C static C* _instance; // Internal data of class C int c; protected: // Constructor of class C C() { c = 0; } public: // A static method that returns an instance of the C class static C* Instance() { if (_instance == nullptr) { // Create instance _instance = (C*) new C(); return _instance; } else { return nullptr; } } // Internal data access methods int Get() { return c; } void Set(int c) { this->c = c; } // Print the value of the internal variable c for control void Print() { cout << "C.c = " << c << endl; } // Class destructor - correct memory release ~C() { delete _instance; } }; // Initialization of the internal static variable _instance // in each of the classes A* A::_instance = nullptr; B* B::_instance = nullptr; C* C::_instance = nullptr; void main() { // Demonstration of using the Singleton pattern for inherited classes // 1. Create the instance of class A A* objA; objA = A::Instance(); if (objA != nullptr) { // if the instance is created, then write the number 330 to it objA->Set(330); objA->Print(); } else cout << "objA == nullptr" << endl; // 2. Trying to create another instance of class A A* objA2; objA2 = A::Instance(); if (objA2 != nullptr) { objA2->Set(550); objA2->Print(); } else cout << "objA2 == nullptr" << endl; // 3. Create an instance of class B B* objB; objB = (B*) B::Instance(); if (objB != nullptr) { objB->Set(770); objB->Print(); objA->Print(); } else cout << "objB == nullptr" << endl; // 4. Trying to create another instance of class B B* objB2 = (B*)B::Instance(); if (objB2 != nullptr) { objB2->Set(2222); objB2->Print(); } else cout << "objB2 == nullptr" << endl; // 5. Create an instance of class C C* objC = (C*)C::Instance(); if (objC != nullptr) { objC->Set(880); objC->Print(); // print the value of the instances objA, objB again objA->Print(); objB->Print(); } else cout << "objC == nullptr" << endl; }
⇑
2.3. The result of the program
The result of the program execution (clause 2.2) is as follows:
A.a = 330 objA2 == nullptr B.b = 770 A.a = 330 objB2 == nullptr C.c = 880 A.a = 330 B.b = 770
⇑
3. An example of spawning and using a subclass in the Singleton pattern. Java implementation
This example shows the implementation of solving a problem in the Java language for three classes A, B, C forming an inheritance hierarchy. The approach to solving the problem is the same as in the previous example in C++ (see Figure 2).
3.1. The text of the program
// The implementation of Singleton pattern in Java for class // which form a hierarchy. // Superclass A class A { private static A _instance=null; // reference to the instance of class A private int a; // internal data // Class constructor protected A() { a = 0; } // A method that returns a single instance of class A to the client public static A Instance() { if (_instance==null) { _instance = new A(); // create an instance of class A return _instance; } else return null; } // Methods to access to class data public void Set(int _a) { a = _a; } public int Get() { return a; } // Internal data output method public void Print() { System.out.println("A.a = " + a); } } // Class B - inherits from class A class B extends A { private static B _instance=null; // the instance of class B private int b; // internal data // the constructor of class B protected B() { b = 0; // initialize internal data } public static B Instance() { if (_instance==null) { _instance = new B(); return _instance; } else return null; } // Access methods for internal data of a class public void Set(int _b) { b = _b; } public int Get() { return b; } // Screen output method public void Print() { System.out.println("B.b = " + b); } } // Class C - inherits from class B class C extends B { private static C _instance=null; private int c; // internal data // The constructor of class C protected C() { c = 0; } // Method that returns an instance of class C public static C Instance() { if (_instance==null) { _instance = new C(); return _instance; } else return null; } // Access methods public void Set(int _c) { c = _c; } public int Get() { return c; } // Screen output method public void Print() { System.out.println("C.c = " + c); } } public class TestSingleton { public static void main(String[] args) { // Demonstration of using the Singleton pattern for 3 classes, // which create a hierarchy // 1. Create the instance of class C C objC = C.Instance(); if (objC!=null) { objC.Set(255); objC.Print(); // C.c = 255 } else System.out.println("objC == null"); // 2. Attempting to create another instance of class C C objC2 = C.Instance(); if (objC2!=null) { objC2.Set(550); objC2.Print(); } else System.out.println("objC2 == null"); // 3. Create the instance of class A A objA = A.Instance(); if (objA!=null) { objA.Set(780); objA.Print(); } else System.out.println("objA == null"); // 4. Re-instantiating class A A objA2 = A.Instance(); if (objA2!=null) { objA2.Set(1000); objA2.Print(); } else System.out.println("objA2 == null"); } }
⇑
3.2. The result of the program
C.c = 255 objC2 == null A.a = 780 objA2 == null
⇑
4. An example of using the Singleton pattern for 3 classes that form a hierarchy. C# implementation
The example implements the use of the Singleton pattern as shown in Figure 2 (C++ solution).
4.1. Program text
using System; namespace ConsoleApp9 { // Base class in the class hierarchy class A { private static A _instance = null; // a reference to the instance of the class private int a; // internal data // Class constructor protected A() { a = 0; } // Method that returns an instance of the class public static A Instance() { if (_instance==null) { _instance = new A(); return _instance; } return null; } // Methods to access the field a public int Get() { return a; } public void Set(int _a) { a = _a; } // Method that displays the value of a on the screen public void Print() { Console.WriteLine("A.a = {0}", a); } } // Class inherited from class A class B:A { private static B _instance; // the instance of class B private int b; // internal data // class constructor protected B() { b = 0; } // Method that returns the instance of the class public static new B Instance() { if (_instance == null) { _instance = new B(); return _instance; } else return null; } // Methods to access a field b public new int Get() { return b; } public new void Set(int _b) { b = _b; } // Method that displays value of to the screen public new void Print() { Console.WriteLine("B.b = {0}", b); } } // Class inheriting from class B class C:B { private static C _instance; // the instance of class C private int c; // internal data // class constructor protected C() { c = 0; } // Method that returns an instance of a class public static new C Instance() { if (_instance == null) { _instance = new C(); return _instance; } else return null; } // Methods to access the field c public new int Get() { return c; } public new void Set(int _c) { c = _c; } // Method that displays the value of c on the screen public new void Print() { Console.WriteLine("C.c = {0}", c); } } // The class that acts as a client class Program { static void Main(string[] args) { // Demonstration of Singleton pattern for 3 inherited classes // 1. Create an instance of class B B objB = B.Instance(); if (objB != null) { objB.Set(330); objB.Print(); } else Console.WriteLine("objB == null"); // 2. Re-instantiate class B B objB2 = B.Instance(); if (objB2 != null) { objB2.Set(700); objB2.Print(); } else Console.WriteLine("objB2 == null"); // 3. Create an instance of class C C objC = C.Instance(); if (objC != null) { objC.Set(880); objC.Print(); } else Console.WriteLine("objC == null"); // 4. Attempt to re-instantiate class C C objC2 = C.Instance(); if (objC2 != null) { objC2.Set(990); objC2.Print(); } else Console.WriteLine("objC2 == null"); } } }
⇑
4.2. The result of the program
After execution the program (see p. 4.1) will give the following result:
B.b = 330 objB2 == null C.c = 880 objC2 == null
⇑
Related topics
⇑