У цій статті розглядаються патерни проєктування в Java — що вони з себе представляють, чому вони важливі та як їх застосовувати в реальних проєктах. Патерни допомагають знаходити ефективні рішення для повторюваних проблем і дозволяють писати чистий та зручний для підтримки код. Вони створюють спільну мову серед розробників, полегшуючи командну роботу.
Ми розглянемо три основні категорії патернів: Творчі (Creational), Структурні (Structural) та Поведенкові (Behavioral). Кожен тип буде розглянутий з прикладами та чіткими поясненнями. Якщо ви вже знайомі з основами Java та об’єктно-орієнтованого програмування, таких як класи та об’єкти, ви готові до цього!
Патерн Сінглтон є частиною творчих патернів і гарантує, що клас має тільки один екземпляр. Це важливо для таких випадків, коли необхідно мати глобальну точку доступу до цього екземпляра, наприклад, для керування конфігураціями, логуванням або пулом підключень.
Проблема
При розробці додатків часто виникає необхідність мати лише один екземпляр певного класу для управління спільними ресурсами. Якщо створити кілька екземплярів такого класу, це може призвести до неефективного використання ресурсів або навіть до конфліктів. Потрібно забезпечити єдиний екземпляр протягом усього часу роботи програми.
Рішення
Сінглтон дозволяє створити тільки один екземпляр класу та забезпечує доступ до нього з будь-якої точки програми. Патерн особливо корисний для керування спільними ресурсами, хоча важливо використовувати його обережно, щоб уникнути проблем із глобальним станом, які можуть ускладнити тестування та підтримку коду.
Структура патерну
- Клас Сінглтон: клас, що гарантує існування лише одного екземпляра.
- Приватний конструктор: він запобігає інстанціації класу з інших місць.
- Приватний статичний екземпляр: зберігає єдиний екземпляр.
- Публічний статичний метод: надає доступ до екземпляра.
Застосування
Використовуйте цей патерн, коли вам потрібно мати єдиний екземпляр класу, доступний у всіх частинах програми, для ефективного керування спільними ресурсами. Однак не рекомендується використовувати Сінглтон в якості стандартного рішення, оскільки надмірне використання може порушити принципи SOLID та призвести до надмірної залежності класів.
Як реалізувати
- Створіть клас Сінглтон із приватним конструктором і приватним статичним екземпляром. Надайте публічний метод для доступу до екземпляра.
public class Singleton {
private static Singleton instance;
private Singleton() {
// Забороняє інстанціацію з інших класів
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public void doSomething() {
System.out.println("Doing something...");
}
}
- Використовуйте екземпляр Сінглтона в іншому коді через метод
getInstance()
.
public class SingletonApp {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2); // Виведе: true
singleton1.doSomething();
}
}
Використання в Spring Framework
Так, Spring активно використовує патерн Сінглтон, але не через статичні методи. В Spring це реалізовано через управління життєвим циклом бінів. За замовчуванням Spring beans мають синглтон-обсяг, що гарантує створення одного екземпляра біну для всього додатку.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public Singleton singleton() {
return new Singleton();
}
}
Використовуючи анотацію @Autowired
, Spring автоматично впроваджує синглтон-бін в інші компоненти:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class SingletonService {
@Autowired
private Singleton singleton;
public void useSingleton() {
singleton.doSomething();
}
}
Реалізація синглтона для багатозадачності
Щоб зробити Сінглтон безпечним для багатозадачності, можна використовувати підхід подвійного блокування або ініціалізацію через холдер на вимогу.
- Подвійне блокування
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
// Забороняє інстанціацію з інших класів
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
public void doSomething() {
System.out.println("Doing something...");
}
}
- Ініціалізація через холдер на вимогу
public class Singleton {
private Singleton() {
// Забороняє інстанціацію з інших класів
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
public void doSomething() {
System.out.println("Doing something...");
}
}
Переваги та недоліки
Переваги:
- Глобальний доступ до екземпляра.
- Ефективне управління ресурсами.
- Контрольована інстанціація.
Недоліки:
- Глобальний стан ускладнює тестування.
- Щільне зв’язування порушує принцип інверсії залежностей.
- Проблеми з багатозадачністю.
- Проблеми з тестуванням через статичні методи.
Цей патерн має багато переваг, але слід бути обережними при його використанні, щоб не створити глобальні залежності та не порушити принципи проектування.
Перекладено з: Creational Patterns: Singleton