Паттерн 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");
}
Задание
ЛегкоДавайте создадим менеджер конфигурации, используя паттерн Одиночка (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, полученное через вторую ссылку
?>В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово $thisМетодыСвойстваКонструктор (__construct)Деструктор (__destruct)Итоги — Простой калькулятор4Наследование
Основы наследованияКлючевое слово parent::Переопределение методовКлючевое слово finalАбстрактные классыИтоги — Иерархия сотрудников7Инкапсуляция
Public, Protected, PrivateПодробный разбор модификаторов доступаГеттеры и сеттерыСокрытие данныхConstructor Promotion (8.0)Итоги — Система учета студентов10Продвинутые концепции ООП
Композиция против наследованияВнедрение зависимостейАнонимные классыПеречисления (PHP 8.1)Файберы (PHP 8.1)Глубокое погружение в клонирование объектовГенераторы и итераторы13Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий2Пространства имен и автозагрузка
Введение в пространства именКлючевое слово useСтандарт автозагрузки PSR-4Автозагрузчик ComposerИтоги: структурированный проект5Интерфейсы и контракты
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовИнтерфейс против абстрактного классаType Hinting с интерфейсамиИтоги — Калькулятор фигур8Магические методы
Введение в магические методы__toString и __debugInfo__get, __set, __isset, __unset__call и __callStatic__clone и клонирование объектов__serialize и __unserializeИтоги — Кастомная коллекция11Система типов и обработка ошибок
Объявление типовNullable-типыТипы объединения и пересеченияКлассы исключенийИерархия пользовательских исключенийTry, Catch, FinallyИтоги — Валидатор форм14Проект: Управление библиотекой
Обзор проектаКлассы Book и User3Свойства классов
Свойства экземпляра и статические свойстваКонстанты в классахСтатические методы и свойстваПриватные и защищенные свойстваСвойства Readonly (PHP 8.1)Итоги — Менеджер банковского счета6Полиморфизм
Снова о переопределении методовПолиморфизм через интерфейсыType Hinting и Union TypesПозднее статическое связываниеИтоги — Payment Processor9Трейты
Введение в трейтыИспользование нескольких трейтовРазрешение конфликтов трейтовАбстрактные методы в трейтахТрейты и наследование12Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy