Паттерн Singleton. Використання у випадку, коли класи-одинаки утворюють ієрархію успадкування. Реалізації на C++, C#, Java
Перед вивченням даної теми рекомендується ознайомитись з наступною темою:
Зміст
- 1. Узагальнена структура паттерну Singleton. Визначення проблеми
- 2. Породження та використання підкласу в паттерні Singleton. Приклад реалізації на C++
- 3. Приклад породження та використання підкласу в паттерні Singleton. Реалізація на Java
- 4. Приклад застосування паттерну Singleton для 3-х класів, що утворюють ієрархію. Реалізація на C#
- Зв’язані теми
Пошук на інших ресурсах:
1. Узагальнена структура паттерну Singleton. Визначення проблеми
Перед використанням прикладу доцільно розглянути спрощену структуру паттерну Singleton.
Рисунок 1. Спрощена структура паттерну Singleton
У паттерні Singleton, з класу-одинака може бути успадковано підкласи, які конкретизують (розширяють) роботу методів (операцій) базового класу. Виникає задача, яка повинна давати відповіді на наступні питання:
- як визначити успадкований клас;
- як зробити, щоб клієнти могли використовувати єдиний екземпляр успадкованого класу. Тут потрібно врахувати, що з базового класу може бути успадковано декілька підкласів.
⇑
2. Породження та використання підкласу в паттерні Singleton. Приклад реалізації на C++
2.1. Схема класів, що утворюють ієрархію
У прикладі демонструється використання паттерну Singleton для трьох класів A, B, C, які утворюють ієрархію успадкування зображену на рисунку 2.
Згідно з умовою задачі потрібно проконтролювати створення не більше одного екземпляру кожного класу. Тобто, клієнт може створити максимум по одному екземпляру кожного з класів (усього 3 екземпляри).
Найпростішим варіантом рішення задачі є додавання в кожен клас власного статичного екземпляру та статичного методу Instance().
Рисунок 2. Схема рішення задачі. Ієрархія класів A, B, C. Статичні методи Instance() в класах
Як видно з рисунку 2, кожен з класів має власний метод Instance(), який створює єдиний екземпляр даного класу. У кожному класі оголошується посилання (покажчик) на єдиний створений екземпляр.
⇑
2.2. Текст програми на мові C++
#include <iostream> using namespace std; // Реалізація паттерну Singleton на C++. // Демонстрація використання паттерну Singleton // у випадку, коли класи утворюють ієрархію успадкування. // Задача. Проконтролювати створення не більше одного екземпляру кожного класу, // які утворюють ієрархію успадкування. class A { private: // Статична внутрішня змінна, яка зберігає екземпляр класу A. static A* _instance; // Деякі дані класу, тут можуть бути будь-які дані int a; protected: // Конструктор A() { a = 0; } public: // Статичний метод, який повертає екземпляр класу A. static A* Instance() { if (_instance == nullptr) { // Створити екземпляр _instance = (A*) new A(); return _instance; } else { return nullptr; } } // Методи (операції) для доступу до внутрішніх даних класу (поля a) int Get() { return a; } void Set(int _a) { a = _a; } // Метод, що виводить значення внутрішнього поля d void Print() { cout << "A.a = " << a << endl; } // Деструктор класу - коректне звільнення пам'яті ~A() { delete[] _instance; } }; // Клас B - успадковує клас A class B : public A { private: static B* _instance; // Деякі внутрішні дані класу B, тут можна помістити власні дані int b; protected: // Захищений конструктор класу B B() { b = 0; } public: // Статичний метод, який повертає екземпляр класу B. static B* Instance() { if (_instance == nullptr) { // Створити екземпляр _instance = (B*) new B(); return _instance; } else { return nullptr; } } // Методи доступу до внутрішніх даних (змінна b) int Get() { return b; } void Set(int _b) { b = _b; } // Вивести значення внутрішньої змінної b для контролю void Print() { cout << "B.b = " << b << endl; } // Деструктор класу - коректне звільнення пам'яті ~B() { delete _instance; } }; // Клас C - успадковує клас B class C :B { private: // Екземпляр класу C static C* _instance; // Внутрішні дані класу C int c; protected: // Захищений конструктор класу C C() { c = 0; } public: // Статичний метод, який повертає екземпляр класу C. static C* Instance() { if (_instance == nullptr) { // Створити екземпляр _instance = (C*) new C(); return _instance; } else { return nullptr; } } // Методи доступу до внутрішніх даних int Get() { return c; } void Set(int c) { this->c = c; } // Вивести значення внутрішньої змінної c для контролю void Print() { cout << "C.c = " << c << endl; } // Деструктор класу - коректне звільнення пам'яті ~C() { delete _instance; } }; // Ініціалізація внутрішньої статичної змінної _instance // в кожному з класів. A* A::_instance = nullptr; B* B::_instance = nullptr; C* C::_instance = nullptr; void main() { // Демонстрація використання паттерну Singleton для успадкованих класів // 1. Створити екземпляр класу A A* objA; objA = A::Instance(); if (objA != nullptr) { // якщо екземпляр створено, то записати в нього число 330 objA->Set(330); objA->Print(); } else cout << "objA == nullptr" << endl; // 2. Спроба створити ще один екземпляр класу A A* objA2; objA2 = A::Instance(); if (objA2 != nullptr) { objA2->Set(550); objA2->Print(); } else cout << "objA2 == nullptr" << endl; // 3. Створити екземпляр класу B B* objB; objB = (B*) B::Instance(); if (objB != nullptr) { objB->Set(770); objB->Print(); objA->Print(); } else cout << "objB == nullptr" << endl; // 4. Спроба створення ще одного екземпляру класу B B* objB2 = (B*)B::Instance(); if (objB2 != nullptr) { objB2->Set(2222); objB2->Print(); } else cout << "objB2 == nullptr" << endl; // 5. Створити екземпляр класу C C* objC = (C*)C::Instance(); if (objC != nullptr) { objC->Set(880); objC->Print(); // вивести значення екземплярів objA, objB повторно objA->Print(); objB->Print(); } else cout << "objC == nullptr" << endl; }
⇑
2.3. Результат роботи програми
Результат роботи програми (п. 2.2) наступний:
A.a = 330 objA2 == nullptr B.b = 770 A.a = 330 objB2 == nullptr C.c = 880 A.a = 330 B.b = 770
⇑
3. Приклад породження та використання підкласу в паттерні Singleton. Реалізація на Java
У даному прикладі наведена реалізація розв’язку задачі на мові Java для трьох класів A, B, C які утворюють ієрархію успадкування. Підхід до рішення задачі такий самий як і в попередньому прикладі на C++ (дивіться рисунок 2).
3.1. Текст програми
// Реалізація паттерну Singleton на Java для класів, // які утворюють ієрархію // Суперклас A class A { private static A _instance=null; // посилання на екземпляр класу A private int a; // внутрішні дані // Захищений конструктор класу protected A() { a = 0; } // Метод, який повертає клієнту єдиний екземпляр класу A public static A Instance() { if (_instance==null) { _instance = new A(); // створити екземпляр класу A return _instance; } else return null; } // Методи доступу до даних класу public void Set(int _a) { a = _a; } public int Get() { return a; } // Метод виведення внутрішніх даних для контролю public void Print() { System.out.println("A.a = " + a); } } // Клас B - успадковує клас A class B extends A { private static B _instance=null; // екземпляр класу B private int b; // внутрішні дані // захищений конструктор класу B protected B() { b = 0; // ініціалізувати внутрішні дані } public static B Instance() { if (_instance==null) { _instance = new B(); return _instance; } else return null; } // Методи доступу до внутрішніх даних класу public void Set(int _b) { b = _b; } public int Get() { return b; } // Метод виведення даних на екран public void Print() { System.out.println("B.b = " + b); } } // Клас C - успадковує клас B class C extends B { private static C _instance=null; private int c; // внутрішні дані // Захищений конструктор класу C protected C() { c = 0; } // Метод, що повертає екземпляр класу C public static C Instance() { if (_instance==null) { _instance = new C(); return _instance; } else return null; } // Методи доступу public void Set(int _c) { c = _c; } public int Get() { return c; } // Метод виведення даних на екран public void Print() { System.out.println("C.c = " + c); } } public class TestSingleton { public static void main(String[] args) { // Демонстрація використання паттерну Singleton для 3-х класів, // які утворюють ієрархію // 1. Створити екземпляр класу C C objC = C.Instance(); if (objC!=null) { objC.Set(255); objC.Print(); // C.c = 255 } else System.out.println("objC == null"); // 2. Спроба створення другого екземпляру класу C C objC2 = C.Instance(); if (objC2!=null) { objC2.Set(550); objC2.Print(); } else System.out.println("objC2 == null"); // 3. Створення екземпляру класу A A objA = A.Instance(); if (objA!=null) { objA.Set(780); objA.Print(); } else System.out.println("objA == null"); // 4. Повторне створення екземпляру класу A A objA2 = A.Instance(); if (objA2!=null) { objA2.Set(1000); objA2.Print(); } else System.out.println("objA2 == null"); } }
⇑
3.2. Результат виконання програми
C.c = 255 objC2 == null A.a = 780 objA2 == null
⇑
4. Приклад застосування паттерну Singleton для 3-х класів, що утворюють ієрархію. Реалізація на C#
У прикладі реалізовано використання паттерну Singleton за зразком, зображени на рисунку 2 (розв’язок на C++).
4.1. Текст програми
using System; namespace ConsoleApp9 { // Базовий клас в ієрархії класів class A { private static A _instance = null; // посилання на екземпляр класу private int a; // внутрішні дані // Захищений конструктор класу protected A() { a = 0; } // Метод, що повертає екземпляр класу public static A Instance() { if (_instance==null) { _instance = new A(); return _instance; } return null; } // Методи доступу до поля a public int Get() { return a; } public void Set(int _a) { a = _a; } // Метод, що виводить значення a на екран public void Print() { Console.WriteLine("A.a = {0}", a); } } // Клас, успадкований від класу A class B:A { private static B _instance; // екземпляр класу B private int b; // внутрішні дані // захищений конструктор класу protected B() { b = 0; } // Метод, що повертає екземпляр класу public static new B Instance() { if (_instance == null) { _instance = new B(); return _instance; } else return null; } // Методи доступу до поля b public new int Get() { return b; } public new void Set(int _b) { b = _b; } // Метод, що виводить значення b на екран public new void Print() { Console.WriteLine("B.b = {0}", b); } } // Клас, що успадковує клас B class C:B { private static C _instance; // екземпляр класу C private int c; // внутрішні дані // захищений конструктор класу protected C() { c = 0; } // Метод, що повертає екземпляр класу public static new C Instance() { if (_instance == null) { _instance = new C(); return _instance; } else return null; } // Методи доступу до поля c public new int Get() { return c; } public new void Set(int _c) { c = _c; } // Метод, що виводить значення c на екран public new void Print() { Console.WriteLine("C.c = {0}", c); } } // Клас, що виступає клієнтом class Program { static void Main(string[] args) { // Демонстрація паттерну Singleton для 3-х успадкованих класів // 1. Створити екземпляр класу B B objB = B.Instance(); if (objB != null) { objB.Set(330); objB.Print(); } else Console.WriteLine("objB == null"); // 2. Повторно створити екземпляр класу B B objB2 = B.Instance(); if (objB2 != null) { objB2.Set(700); objB2.Print(); } else Console.WriteLine("objB2 == null"); // 3. Створити екземпляр класу C C objC = C.Instance(); if (objC != null) { objC.Set(880); objC.Print(); } else Console.WriteLine("objC == null"); // 4. Спроба повторного створення екземпляру класу C C objC2 = C.Instance(); if (objC2 != null) { objC2.Set(990); objC2.Print(); } else Console.WriteLine("objC2 == null"); } } }
⇑
4.2. Результат виконання програми
Після виконання програма (дивіться п. 4.1.) видасть наступний результат:
B.b = 330 objB2 == null C.c = 880 objC2 == null
⇑
Зв’язані теми
⇑