Menu
Coddy logo textTech

Паттерн Singleton

Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy — урок 71 из 91.

Паттерн Одиночка (Singleton Pattern) — это порождающий паттерн проектирования, который гарантирует, что у класса есть только один экземпляр на протяжении всего жизненного цикла вашего приложения. Он также предоставляет глобальную точку доступа к этому экземпляру.

Этот паттерн полезен, когда вам нужен ровно один объект для координации действий в системе, такой как соединение с базой данных, менеджер конфигурации или логгер. Создание нескольких экземпляров таких объектов привело бы к пустой трате ресурсов или несогласованному поведению.

Вот как реализовать Singleton в PHP:

<?php
class Database {
    private static ?Database $instance = null;
    
    private function __construct() {
        // Приватный конструктор предотвращает прямое создание экземпляра
    }
    
    public static function getInstance(): Database {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    public function query(string $sql): string {
        return "Executing: $sql";
    }
}

$db1 = Database::getInstance();
$db2 = Database::getInstance();

echo $db1 === $db2 ? "Same instance" : "Different instances";

Вывод:

Same instance

Паттерн опирается на три ключевых элемента: приватный конструктор, который предотвращает прямое использование new, статическое свойство, которое хранит единственный экземпляр, и статический метод, который создает экземпляр при первом вызове и возвращает его при последующих вызовах.

Чтобы сделать Одиночку (Singleton) по-настоящему пуленепробиваемым, вам также следует предотвратить клонирование и десериализацию:

<?php
private function __clone() {}
public function __wakeup() {
    throw new Exception("Cannot unserialize singleton");
}
challenge icon

Задание

Легко

Давайте создадим менеджер конфигурации, используя паттерн Одиночка (Singleton). В реальных приложениях обычно требуется единый источник истины для настроек конфигурации — наличие нескольких объектов конфигурации может привести к несогласованному поведению всей системы.

Вы организуете свой код в двух файлах:

  • Config.php — Создайте класс Config, который реализует паттерн Singleton. Ваш менеджер конфигурации должен:
    • Хранить настройки в приватном свойстве-массиве
    • Иметь приватный конструктор, который инициализирует массив настроек как пустой
    • Предоставлять статический метод getInstance(), который возвращает единственный экземпляр
    • Предотвращать клонирование, сделав метод __clone() приватным
    • Включать метод set(string $key, string $value): void для сохранения настройки
    • Включать метод get(string $key): ?string, который возвращает значение по ключу или null, если ключ не существует
    • Включать метод getAll(): array, который возвращает все настройки
  • main.php — Подключите файл Config. Вы получите два входных значения: ключ и значение.

    Получите экземпляр Config и установите настройку по умолчанию с ключом "app_name" и значением "MyApp".

    Получите вторую ссылку на экземпляр Config (снова используя getInstance()) и используйте её для установки другой настройки, используя ключ и значение из входных данных.

    Наконец, проверьте, правильно ли работает Singleton:

    • Выведите "Same instance: true" или "Same instance: false" в зависимости от того, указывают ли обе ссылки на один и тот же объект (используйте === для сравнения)
    • Выведите "Settings count: [count]", показывая общее количество сохраненных настроек (используйте count() для результата getAll())
    • Выведите "App name: [value]", показывая настройку app_name, полученную через вторую ссылку

Эта задача демонстрирует основное преимущество паттерна Singleton: сколько бы раз вы ни вызывали getInstance(), вы всегда получаете один и тот же объект, что гарантирует использование одного и того же состояния конфигурации всеми частями вашего приложения.

Шпаргалка

Паттерн Singleton (Одиночка) гарантирует, что у класса есть только один экземпляр на протяжении всего жизненного цикла приложения, и предоставляет глобальную точку доступа к этому экземпляру.

Этот паттерн полезен для таких объектов, как подключения к базе данных, менеджеры конфигураций или логгеры, где создание нескольких экземпляров привело бы к пустой трате ресурсов или несогласованному поведению.

Базовая реализация Singleton:

<?php
class Database {
    private static ?Database $instance = null;
    
    private function __construct() {
        // Приватный конструктор предотвращает прямое создание экземпляра
    }
    
    public static function getInstance(): Database {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}

$db1 = Database::getInstance();
$db2 = Database::getInstance();

echo $db1 === $db2 ? "Same instance" : "Different instances";
// Вывод: Same instance

Ключевые элементы:

  • Приватный конструктор — предотвращает использование new напрямую
  • Статическое свойство — хранит единственный экземпляр
  • Статический метод — создает экземпляр при первом вызове и возвращает его при последующих вызовах

Предотвращение клонирования и десериализации:

<?php
private function __clone() {}

public function __wakeup() {
    throw new Exception("Cannot unserialize singleton");
}

Попробуйте сами

<?php

require_once 'Config.php';

// Чтение входных данных
$key = trim(fgets(STDIN));
$value = trim(fgets(STDIN));

// TODO: Получите экземпляр Config и установите "app_name" в "MyApp"

// TODO: Получите вторую ссылку на Config, снова используя getInstance()

// TODO: Используйте вторую ссылку для установки настройки, используя $key и $value из входных данных

// TODO: Проверьте, работает ли Singleton:
// - Выведите "Same instance: true" или "Same instance: false" на основе сравнения ===
// - Выведите "Settings count: [count]", показывая общее количество настроек
// - Выведите "App name: [value]", показывая app_name, полученное через вторую ссылку

?>
quiz iconПроверьте себя

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

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