Patterns. Singleton pattern. Usage when single classes form an inheritance hierarchy. C++, C#, Java implementations




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


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).

Simplified structure of the Singleton pattern

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.

The Singleton pattern. Class hierarchy. Static methods Instance() in classes. Implementation in C++

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