logo

Шаблон проектування методу Singleton

Шаблон Singleton, мабуть, найбільш широко використовуваний шаблон проектування. Це простий шаблон, який легко зрозуміти та використовувати. Іноді його використовують у надлишку та в сценаріях, де це не потрібно. У таких випадках недоліки його використання переважують переваги, які він приносить. З цієї причини одиночний шаблон іноді вважається антипаттерн або шаблон синглтон .

Singleton-Method-Design-Pattern



Важливі теми для шаблону проектування методу Singleton

1. Що таке Singleton Method Design Pattern?

Метод Singleton або шаблон Singleton Design є одним із найпростіших шаблонів проектування. Це гарантує, що клас має лише один екземпляр, і забезпечує глобальну точку доступу до нього.

2. Коли використовувати шаблон проектування методу Singleton?

Використовуйте метод Singleton Design Pattern, коли:



  • Повинен бути рівно один екземпляр класу, і він повинен бути доступним для клієнтів із добре відомої точки доступу.
  • Коли єдиний екземпляр має бути розширюваним за допомогою підкласів, а клієнти повинні мати можливість використовувати розширений екземпляр без змін
  • Класи Singleton використовуються для журналювання, об’єктів драйверів, кешування та пулу потоків, підключень до бази даних.

3. Типи ініціалізації Singleton

Клас Singleton може бути створений двома методами:

  • Рання ініціалізація: У цьому методі клас ініціалізується незалежно від того, використовувати його чи ні. Головна перевага цього методу – його простота. Ви ініціюєте клас під час завантаження класу. Його недоліком є ​​те, що клас завжди ініціалізується незалежно від того, використовується він чи ні.
  • Лінива ініціалізація: У цьому методі клас ініціалізується лише тоді, коли це потрібно. Це може позбавити вас від створення екземпляра класу, коли він вам не потрібен. Як правило, лінива ініціалізація використовується, коли ми створюємо клас singleton.

4. Ключовий компонент шаблону проектування методу Singleton:

Key-Component-of-Singleton-Method-Design-Pattern-(1)

4.1. Статичний член:

Шаблон Singleton або шаблон Singleton використовує статичний член у класі. Цей статичний член гарантує, що пам’ять виділяється лише один раз, зберігаючи єдиний екземпляр класу Singleton.



Java
// Static member to hold the single instance private static Singleton instance;>

4.2. Приватний забудовник:

Шаблон Singleton або шаблон Singleton містить приватний конструктор, який служить барикадою проти зовнішніх спроб створити екземпляри класу Singleton. Це гарантує, що клас має контроль над своїм процесом інстанціювання.

Java
// Private constructor to // prevent external instantiation class Singleton {  // Making the constructor as Private  private Singleton()  {  // Initialization code here  } }>

4.3. Статичний заводський метод:

Вирішальним аспектом шаблону Сінглтон є наявність статичного фабричного методу. Цей метод діє як шлюз, надаючи глобальну точку доступу до об’єкта Singleton. Коли хтось запитує екземпляр, цей метод або створює новий екземпляр (якщо його не існує), або повертає існуючий екземпляр абоненту.

diff у python
Java
// Static factory method for global access public static Singleton getInstance() {  // Check if an instance exists  if (instance == null) {  // If no instance exists, create one  instance = new Singleton();  }  // Return the existing instance  return instance; }>

5. Реалізація шаблону проектування методу Singleton

Реалізація Singleton Design Pattern або Pattern Singleton описана на наступній діаграмі класів:

Скріншот-2023-12-07-174635

Реалізація шаблону проектування методу Singleton

Реалізація однотонного шаблону Design дуже проста і складається з одного класу. Щоб переконатися, що одиночний екземпляр є унікальним, усі конструктори одного елемента мають бути закритими. Глобальний доступ здійснюється за допомогою статичного методу, до якого можна отримати глобальний доступ до одного екземпляра, як показано в коді.

Java
/*package whatever //do not write package name here */ import java.io.*; class Singleton {  // static class  private static Singleton instance;  private Singleton()  {  System.out.println('Singleton is Instantiated.');  }  public static Singleton getInstance()  {  if (instance == null)  instance = new Singleton();  return instance;  }  public static void doSomething()  {  System.out.println('Somethong is Done.');  } } class GFG {  public static void main(String[] args)  {  Singleton.getInstance().doSomething();  } }>

Вихід
Singleton is Instantiated. Somethong is Done.>

Метод getInstance, ми перевіряємо, чи є екземпляр нульовим. Якщо екземпляр не нульовий, це означає, що об’єкт був створений раніше; інакше ми створюємо його за допомогою оператора new.

оператор if java

6. Різні способи реалізації шаблону проектування Singleton Method

Іноді нам потрібно мати лише один екземпляр нашого класу, наприклад, одне з’єднання з БД, спільне для кількох об’єктів, оскільки створення окремого з’єднання з БД для кожного об’єкта може бути дорогим. Подібним чином у програмі може бути один менеджер конфігурації або диспетчер помилок, який обробляє всі проблеми замість створення кількох менеджерів.

Класична реалізація

Давайте розглянемо різні варіанти дизайну для реалізації такого класу. Якщо ви добре володієте статичними змінними класу та модифікаторами доступу, це не повинно бути складним завданням.

Спосіб 1 – Класична реалізація || Зробіть getInstance() статичним для реалізації Шаблон проектування методу Singleton

Java
// Classical Java implementation of singleton // design pattern class Singleton {  private static Singleton obj;  // private constructor to force use of  // getInstance() to create Singleton object  private Singleton() {}  public static Singleton getInstance()  {  if (obj == null)  obj = new Singleton();  return obj;  } }>

Ось ми і заявили getInstance() static, щоб ми могли викликати його без створення екземпляра класу. Вперше getInstance() називається він створює новий єдиний об’єкт і після цього просто повертає той самий об’єкт.

Примітка: Singleton obj не створюється, поки він нам не знадобиться і не викличемо getInstance() метод. Це називається відкладеним створенням екземплярів. Основна проблема вищевказаного методу полягає в тому, що він небезпечний для потоків. Розглянемо наступну послідовність виконання.

Ця послідовність виконання створює два об’єкти для синглтона. Тому ця класична реалізація не є потокобезпечною.

Спосіб 2 || Зробити getInstance() синхронізованим для реалізації Шаблон проектування методу Singleton

Java
// Thread Synchronized Java implementation of // singleton design pattern class Singleton {  private static Singleton obj;  private Singleton() {}  // Only one thread can execute this at a time  public static synchronized Singleton getInstance()  {  if (obj == null)  obj = new Singleton();  return obj;  } }>

Тут використання synchronized забезпечує виконання лише одного потоку за раз getInstance() . Основним недоліком цього методу є те, що використання synchronized кожного разу під час створення об’єкта singleton є дорогим і може знизити продуктивність вашої програми. Однак, якщо продуктивність о getInstance() не є критичним для вашої програми, цей метод забезпечує чисте та просте рішення.

Метод 3 – Еагер інстанціювання || Реалізація єдиного шаблону проектування на основі статичного ініціалізатора

Java
// Static initializer based Java implementation of // singleton design pattern class Singleton {  private static Singleton obj = new Singleton();  private Singleton() {}  public static Singleton getInstance() { return obj; } }>

Тут ми створили екземпляр singleton у статичному ініціалізаторі. JVM виконує статичний ініціалізатор, коли клас завантажується, і, отже, це гарантовано буде потоково безпечним. Використовуйте цей метод лише тоді, коли ваш одиночний клас легкий і використовується під час виконання вашої програми.

Метод 4 – найефективніший || Використовуйте блокування з подвійною перевіркою, щоб реалізувати одиночний шаблон проектування

Якщо ви уважно помітили, щойно об’єкт створено, синхронізація більше не буде корисною, тому що тепер obj не матиме значення null і будь-яка послідовність операцій призведе до узгоджених результатів. Отже, ми отримаємо блокування getInstance() лише один раз, коли obj має значення null. Таким чином ми синхронізуємо лише перший шлях, саме те, що ми хочемо.

Java
// Double Checked Locking based Java implementation of // singleton design pattern class Singleton {  private static volatile Singleton obj = null;  private Singleton() {}  public static Singleton getInstance()  {  if (obj == null) {  // To make thread safe  synchronized (Singleton.class)  {  // check again as multiple threads  // can reach above step  if (obj == null)  obj = new Singleton();  }  }  return obj;  } }>

Ми оголосили об’єкт летючий який гарантує, що кілька потоків пропонують змінну obj правильно, коли вона ініціалізується для екземпляра Singleton. Цей метод суттєво зменшує накладні витрати на щоразовий виклик синхронізованого методу.

7. Випадок використання методу шаблону Singleton

  • Підключення до бази даних: У програмах, де створення та керування підключеннями до бази даних є дорогою операцією, Singleton можна використовувати для підтримки єдиного підключення до бази даних у всій програмі.
  • Керування конфігурацією: Якщо у вас є параметри глобальної конфігурації, до яких мають отримати доступ різні компоненти програми, диспетчер конфігурації Singleton може забезпечити єдину точку доступу до цих параметрів.
  • Компоненти GUI: Для компонентів або контролерів графічного інтерфейсу користувача (GUI) Singleton може допомогти керувати станом і діями UI, надаючи єдину точку керування.
  • Диспетчери пристроїв: У вбудованих системах або програмах, які взаємодіють з апаратними пристроями, Singleton можна використовувати для керування та контролю доступу до апаратних пристроїв, щоб уникнути конфліктів.
  • Послуги друку: У системах, які включають друк документів або звітів, служба друку Singleton може координувати та керувати завданнями друку, забезпечуючи ефективне використання ресурсів друку.

8. Переваги шаблону проектування методу Singleton:

  • Вирішує колізії імен: У сценаріях, де необхідна єдина точка керування, щоб уникнути конфліктів імен або зіткнень, шаблон Singleton гарантує наявність лише одного екземпляра з унікальним іменем.
  • Нетерпляча або лінива ініціалізація: Патерн Singleton підтримує як активну ініціалізацію (створення екземпляра під час завантаження класу), так і відкладену ініціалізацію (створення екземпляра під час першого запиту), забезпечуючи гнучкість залежно від варіанту використання.
  • Безпека ниток: Правильно реалізовані шаблони Singleton можуть забезпечити безпеку потоку, гарантуючи, що екземпляр буде створено атомарно і що кілька потоків випадково не створюватимуть дублікати екземплярів.
  • Зменшений обсяг пам'яті: У програмах, де споживання ресурсів є критичним, шаблон Singleton може сприяти зменшенню обсягу пам’яті, забезпечуючи наявність лише одного екземпляра класу.

9. Недоліки Singleton Design Pattern

  • Труднощі тестування: Оскільки Singletons вводять глобальний стан, модульне тестування може стати складним завданням. Тестування одного компонента окремо може бути складнішим, якщо воно покладається на Singleton, оскільки стан Singleton може вплинути на результат тестів.
  • Проблеми паралельності: У багатопоточному середовищі можуть виникнути проблеми, пов’язані зі створенням та ініціалізацією примірника Singleton. Якщо кілька потоків намагаються створити Singleton одночасно, це може призвести до умов змагання.
  • Обмежена розширюваність: Шаблон Singleton може зробити код менш розширюваним. Якщо пізніше ви вирішите, що вам потрібні кілька екземплярів класу або захочете змінити логіку створення екземплярів, це може вимагати значного рефакторингу.
  • Глобальна залежність: Шаблон Singleton створює глобальну залежність, що ускладнює заміну Singleton альтернативною реалізацією або використання ін’єкції залежностей для надання екземплярів.
  • Важко підкласити: Підкласи Singleton можуть бути складними. Оскільки конструктор зазвичай приватний, розширення Singleton вимагає додаткової обережності та може не відповідати стандартним шаблонам успадкування.
  • Управління життєвим циклом: Шаблон Singleton може не працювати зі сценаріями, коли примірник потрібно явно знищити або скинути. Управління життєвим циклом Singleton може стати проблемою.
  • Зловживання глобальною точкою доступу: Хоча глобальна точка доступу є перевагою, нею також можна зловживати. У розробників може виникнути спокуса використовувати Singleton для всього, що призведе до надмірного використання глобального стану та менш модульного дизайну.

10. Висновок

Для деяких класів важливо мати рівно один екземпляр. Хоча в системі може бути багато принтерів, має бути лише один спулер принтера. Має бути лише одна файлова система та один менеджер вікон. Цифровий фільтр матиме один аналого-цифровий перетворювач. Бухгалтерська система буде призначена для обслуговування однієї компанії. Як ми гарантуємо, що клас має лише один екземпляр і що екземпляр легко доступний? Глобальна змінна робить об’єкт доступним, але не заважає створювати кілька об’єктів.

Кращим рішенням є зробити клас самим відповідальним за відстеження свого єдиного екземпляра. Клас може гарантувати, що жоден інший екземпляр не може бути створений (шляхом перехоплення запитів на створення нових об’єктів), і він може забезпечити спосіб доступу до екземпляра. Це шаблон Сінглтона.