Menu
Coddy logo textTech

Паттерн 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 метод, который возвращает этот экземпляр. Такая комбинация гарантирует, что независимо от того, сколько раз вы запрашиваете объект, вы всегда получаете один и тот же экземпляр.

challenge icon

Задание

Легко

Давайте создадим менеджер конфигурации для приложения, используя паттерн Одиночка (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
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование