Паттерн Singleton
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy — урок 65 из 87.
Паттерн Одиночка (Singleton Pattern) — это порождающий паттерн проектирования, который гарантирует, что у класса есть только один экземпляр во всем приложении, предоставляя при этом глобальную точку доступа к этому экземпляру. Это полезно для ресурсов, которые должны быть общими, таких как менеджеры конфигурации, пулы соединений или сервисы логирования.
Паттерн работает за счет того, что конструктор делается приватным, предотвращая прямое создание экземпляра. Вместо этого статический метод управляет доступом к единственному экземпляру:
public class DatabaseConnection {
private static DatabaseConnection instance;
// Приватный конструктор предотвращает прямое создание экземпляра
private DatabaseConnection() {
System.out.println("Connection created");
}
// Глобальная точка доступа
public static DatabaseConnection getInstance() {
if (instance == null) {
instance = new DatabaseConnection();
}
return instance;
}
public void query(String sql) {
System.out.println("Executing: " + sql);
}
}Каждый вызов getInstance() возвращает один и тот же объект. Экземпляр создается только при первом запросе — это называется ленивой инициализацией:
DatabaseConnection db1 = DatabaseConnection.getInstance();
DatabaseConnection db2 = DatabaseConnection.getInstance();
System.out.println(db1 == db2); // true - тот же экземплярКлючевыми компонентами Singleton являются: private static поле, хранящее экземпляр, private конструктор и public static метод, который возвращает этот экземпляр. Такая комбинация гарантирует, что независимо от того, сколько раз вы запрашиваете объект, вы всегда получаете один и тот же экземпляр.
Задание
ЛегкоДавайте создадим менеджер конфигурации для приложения, используя паттерн Одиночка (Singleton)! Ваш менеджер конфигурации обеспечит совместное использование одних и тех же настроек всеми частями вашего приложения, независимо от того, сколько раз они запрашивают доступ к нему.
Вы организуете свой код в двух файлах:
AppConfig.java: Создайте класс Singleton, который управляет настройками конфигурации приложения. ВашAppConfigдолжен иметь:Приватное статическое поле для хранения единственного экземпляра.
Приватное строковое поле (String) с именем
appNameи приватное целочисленное поле (int) с именемmaxUsers.Приватный конструктор, который инициализирует
appNameзначением"MyApplication"иmaxUsersзначением100, а затем выводитAppConfig initialized.Публичный статический метод
getInstance(), который создает экземпляр только в том случае, если он еще не существует (ленивая инициализация), а затем возвращает его.Метод
setAppName(String name), который обновляет название приложения.Метод
setMaxUsers(int max), который обновляет максимальное количество пользователей.Метод
displaySettings(), который выводитApp: [appName], Max Users: [maxUsers].Main.java: Продемонстрируйте, что ваш Singleton работает правильно! Вы получите два входных значения: новое название приложения (String) и новое значение максимального количества пользователей (integer).Сначала получите экземпляр
AppConfigи сохраните его в переменной с именемconfig1. ВызовитеdisplaySettings(), чтобы показать значения по умолчанию.Затем обновите настройки, используя входные значения, вызвав
setAppNameиsetMaxUsersдляconfig1.Получите другой экземпляр и сохраните его в
config2. ВызовитеdisplaySettings()дляconfig2, чтобы доказать, что он отражает изменения, внесенные черезconfig1.Наконец, выведите, указывают ли обе ссылки на один и тот же объект:
Same instance: [true/false], сравнивconfig1 == config2.
Вы получите два входных значения по порядку: новое название приложения (String) и новое максимальное количество пользователей (integer).
Эта задача демонстрирует основное преимущество паттерна Singleton: независимо от того, сколько раз вы запрашиваете конфигурацию, вы всегда получаете один и тот же экземпляр с тем же общим состоянием!
Шпаргалка
Паттерн Одиночка (Singleton) гарантирует, что у класса есть только один экземпляр во всем приложении, предоставляя глобальную точку доступа к этому экземпляру.
Ключевые компоненты:
- Поле
private static, содержащее единственный экземпляр - Конструктор
privateдля предотвращения прямого создания экземпляра - Метод
public static, который возвращает этот экземпляр
Базовая реализация:
public class DatabaseConnection {
private static DatabaseConnection instance;
// Приватный конструктор предотвращает прямое создание экземпляра
private DatabaseConnection() {
System.out.println("Connection created");
}
// Глобальная точка доступа
public static DatabaseConnection getInstance() {
if (instance == null) {
instance = new DatabaseConnection();
}
return instance;
}
public void query(String sql) {
System.out.println("Executing: " + sql);
}
}Использование:
DatabaseConnection db1 = DatabaseConnection.getInstance();
DatabaseConnection db2 = DatabaseConnection.getInstance();
System.out.println(db1 == db2); // true - same instanceЭкземпляр создается только при первом запросе — это называется ленивой инициализацией. Каждый вызов getInstance() возвращает один и тот же объект, обеспечивая общее состояние во всем приложении.
Попробуйте сами
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Чтение входных данных
String newAppName = scanner.nextLine();
int newMaxUsers = scanner.nextInt();
// TODO: Получите первый экземпляр AppConfig и сохраните его в config1
// TODO: Вызовите displaySettings() для config1, чтобы показать значения по умолчанию
// TODO: Обновите настройки, используя setAppName и setMaxUsers для config1
// TODO: Получите другой экземпляр и сохраните его в config2
// TODO: Вызовите displaySettings() для config2, чтобы доказать, что изменения являются общими
// TODO: Выведите, указывают ли обе ссылки на один и тот же объект:
// "Same instance: [true/false]" используя config1 == config2
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово thisМетодыПоля (атрибуты)Метод-конструкторПерегрузка конструкторовИтоги — Простой калькулятор4Наследование
Основы наследования (extends)Ключевое слово superПереопределение методов (@Override)Цепочка конструкторовКласс ObjectОдиночное и многоуровневое наследованиеПочему нет множественного наследования классовИтоги — Иерархия сотрудников7Специальные методы и класс Object
Метод toString()equals() и hashCode()Метод clone()compareTo() и ComparableИнтерфейс ComparatorПовторение — Кастомная сортировка2Модификаторы доступа и инкапсуляция
Обзор уровней доступаМетоды геттеры и сеттерыСокрытие данныхКлючевое слово finalИтоги — Менеджер банковского счета5Полиморфизм
Основы перегрузки методовПереопределение методов (Run-Time)Upcasting и DowncastingОператор instanceofАбстрактные классы и методыИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияАгрегация против композицииВнутренние, вложенные и анонимные классыEnums и методы перечисленийRecords (Java 16+)Sealed-классы (Java 17+)11Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy3Свойства класса и статические члены
Переменные экземпляра и статические переменныеСтатические методыСтатические блокиКонстанты (static final)Итоги: Счетчик и утилиты6Интерфейсы и абстрактные классы
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовDefault и Static методы в интерфейсахАбстрактные классы vs ИнтерфейсыФункциональные интерфейсыИтоги — Платежная система9Обобщения
Введение в обобщенияОбобщенные классыОбобщенные методыОграниченные параметры типаСимволы подстановки (Wildcards: ?, extends, super)Итоги — Обобщенный контейнер12Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»