Паттерн Singleton
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy. Урок 65 из 87.
Singleton Pattern — это порождающий шаблон проектирования, который гарантирует, что в вашем приложении у класса будет только один экземпляр, и при этом предоставляет глобальную точку доступа к этому экземпляру. Это полезно для ресурсов, которыми следует делиться, например менеджеров конфигурации, пулов соединений или служб ведения журналов.
Паттерн работает за счёт того, что делает конструктор приватным, предотвращая прямую instantiation. Вместо этого статический метод управляет доступом к единственному экземпляру:
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() возвращает один и тот же объект. Экземпляр создаётся только при первом запросе. Это называется lazy initialization:
DatabaseConnection db1 = DatabaseConnection.getInstance();
DatabaseConnection db2 = DatabaseConnection.getInstance();
System.out.println(db1 == db2); // true - same instanceКлючевые компоненты Singleton: поле private static, содержащее instance, конструктор private и метод public static, возвращающий instance. Эта комбинация гарантирует, что независимо от того, сколько раз вы запрашиваете объект, вы всегда получаете один и тот же объект.
Задание
ЛегкоДавайте создадим менеджер конфигурации для приложения с использованием паттерна 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) и новое максимальное количество пользователей (целое число).Сначала получите экземпляр
AppConfigи сохраните его в переменной с именемconfig1. ВызовитеdisplaySettings(), чтобы показать значения по умолчанию.Затем обновите настройки, используя введённые значения, вызвав
setAppNameиsetMaxUsersдляconfig1.Получите другой экземпляр и сохраните его в
config2. ВызовитеdisplaySettings()дляconfig2, чтобы доказать, что он отражает изменения, внесённые черезconfig1.Наконец, выведите информацию о том, указывают ли обе ссылки на один и тот же объект:
Same instance: [true/false], сравнивconfig1 == config2.
Вы получите два входных значения по порядку: новое имя приложения (String) и новое максимальное количество пользователей (целое число).
Эта задача демонстрирует основное преимущество паттерна Singleton: независимо от того, сколько раз вы запрашиваете конфигурацию, вы всегда получаете один и тот же экземпляр с одним и тем же общим состоянием!
Попробуйте сами
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]" using 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
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»Потренируйтесь самостоятельно: Онлайн-компилятор Java