Паттерн "Одиночка" (Singleton) — один из самых известных порождающих паттернов проектирования. Его основная цель — гарантировать, что у класса будет только один-единственный экземпляр, и предоставить к нему глобальную точку доступа. Это бывает полезно для объектов, которые должны быть в системе в единственном экземпляре, например, для объекта подключения к базе данных, логгера или менеджера конфигурации.
В этом тренажере мы разберем ключевые компоненты этого паттерна. Вы научитесь правильно его конструировать, находить и исправлять типичные ошибки в его реализации, а также предсказывать поведение кода, который его использует. Задания построены от простого к сложному: от сопоставления понятий до сборки целого класса и анализа его работы.
- Модуль 1: Основы синтаксиса PHP
- Модуль 2: Переменные и типы данных
- Модуль 3: Операторы
- Модуль 4: Условные конструкции
- Модуль 5: Циклы
- Модуль 6: Массивы
- Индексированные массивы.
- Ассоциативные массивы.
- Многомерные массивы.
- Добавление элементов в массив.
- Удаление элементов из массива.
- Функция count().
- Функция array_push() и array_pop().
- Функция array_shift() и array_unshift().
- Функция array_merge().
- Функция array_slice().
- Функция array_splice().
- Функция in_array().
- Функция array_search().
- Функция array_keys() и array_values().
- Функция array_unique().
- Функция array_reverse().
- Сортировка: sort(), rsort().
- Сортировка: asort(), arsort().
- Сортировка: ksort(), krsort().
- Функция array_map().
- Функция array_filter().
- Функция array_reduce().
- Функция array_walk().
- Функция array_column().
- Деструктуризация массивов.
- Модуль 7: Функции
- Объявление функций.
- Вызов функций.
- Параметры функций.
- Возврат значений return.
- Значения параметров по умолчанию.
- Передача по значению.
- Передача по ссылке.
- Переменное количество аргументов.
- Оператор распаковки ....
- Типизация параметров.
- Типизация возвращаемых значений.
- Nullable типы.
- Union типы (PHP 8).
- Именованные аргументы (PHP 8).
- Анонимные функции.
- Стрелочные функции.
- Замыкания и use.
- Рекурсивные функции.
- Глобальные переменные в функциях.
- Статические переменные в функциях.
- Модуль 8: Работа со строками
- Одинарные и двойные кавычки.
- Экранирование символов.
- Heredoc и Nowdoc синтаксис.
- Функция strlen().
- Функция substr().
- Функция str_replace().
- Функция strpos() и strrpos().
- Функция explode() и implode().
- Функция trim(), ltrim(), rtrim().
- Функция strtolower() и strtoupper().
- Функция ucfirst() и ucwords().
- Функция str_repeat().
- Модуль 9: Суперглобальные переменные
- Модуль 10: Работа с формами
- Модуль 11: Работа с файлами
- Модуль 12: Дата и время
- Модуль 13: Регулярные выражения
- Модуль 14: Сессии и Cookie
- Модуль 15: Include и Require
- Модуль 16: Объектно-ориентированное программирование
- Модуль 17: Пространства имен
- Модуль 18: Обработка ошибок и исключений
- Модуль 19: Работа с JSON и XML
- Модуль 20: Работа с базами данных MySQLi
- Модуль 21: PDO - PHP Data Objects
- Модуль 22: Composer и зависимости
- Модуль 23: cURL и HTTP запросы
- Модуль 24: REST API
- Модуль 25: Безопасность
- Модуль 26: Генераторы и итераторы
- Модуль 27: Reflection API
- Модуль 28: Работа с изображениями
- Модуль 29: Отправка email
- Модуль 30: Паттерны проектирования
- Singleton паттерн.
- Factory паттерн.
- Модуль 31: Тестирование
- Модуль 32: Продвинутые возможности PHP 8+
Компоненты паттерна Singleton
Паттерн "Одиночка" состоит из нескольких ключевых элементов, каждый из которых выполняет свою строгую функцию. Сопоставьте компонент паттерна с его назначением, чтобы понять, как они вместе обеспечивают создание единственного экземпляра класса.
Соберите основу Singleton из банка
Перед вами — "скелет" класса Singleton. Используйте готовые токены из банка, чтобы правильно объявить статическое свойство для хранения экземпляра и сделать конструктор приватным. Это два первых шага к правильной реализации паттерна.
class Logger
{
input1S input2S $instance;
input3S function input4S()
{
// Конструктор намеренно оставлен пустым
}
}Реализация ленивой инициализации
Дополните метод `getInstance()`. Он должен проверять, создан ли уже экземпляр класса. Если нет — создать его, если да — вернуть существующий. Этот механизм называется "ленивой" (или отложенной) инициализацией.
class Connection
{
private static $instance;
private function __construct() {}
private function __clone() {}
public static function getInstance()
{
if (input1S === null) {
input2S = new input3S();
}
return input4S;
}
}Найдите фатальную ошибку
В этом коде допущена критическая ошибка, которая полностью нарушает принцип Singleton, позволяя создавать множество экземпляров класса. Найдите строку с ошибкой и исправьте ее так, чтобы конструктор стал недоступен извне.
class Settings { private static $instance; public function __construct() { // Создание объекта разрешено // ... } public static function getInstance() { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; }} $settings1 = new Settings(); // Это не должно работать!$settings2 = new Settings();Соберите полный класс Singleton
Восстановите правильную структуру класса Singleton, перетащив строки из левой панели в правую. В банке есть лишние строки, которые содержат логические или синтаксические ошибки. Будьте внимательны и выбирайте только те, что формируют корректный и защищенный "Одиночку".
private static $instance = null;} private function __clone() { }class ConfigReader { public static function getInstance() { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } public function __construct() { } private function __construct() { }return new self();private $instance;Что покажет логгер?
В коде создаются два объекта логгера через метод `getInstance()`. В первый объект добавляется сообщение, а затем второму объекту дается команда записать все сообщения. Что в итоге будет выведено на экран? Введите результат в поле.
// Представьте, что класс Logger реализован как Singleton
// и имеет методы addMessage() и getMessages()
$log1 = Logger::getInstance();
$log1->addMessage("User logged in.");
$log2 = Logger::getInstance();
$log2->addMessage("Data saved.");
echo implode(" ", $log1->getMessages());Защита от десериализации
Кроме клонирования, есть еще один способ "сломать" Singleton — через сериализацию и десериализацию. При десериализации может быть создан новый объект в обход `getInstance()`. Чтобы этого избежать, нужно "перехватить" магический метод `__wakeup`. В коде он есть, но с ошибкой. Исправьте ее, чтобы при попытке десериализации выбрасывалось исключение.
class CacheManager{ private static $instance; // ... пропущены __construct, __clone, getInstance private function __wakeup() // Неверный модификатор доступа { throw new \Exception("Cannot unserialize a singleton."); }} // Попытка обойти Singleton$cache = CacheManager::getInstance();$serialized = serialize($cache);$newCache = unserialize($serialized); // Здесь должно быть исключениеПопытка прямого создания объекта
Код пытается создать экземпляр класса `DBConnector` напрямую, используя оператор `new`. Что произойдет в результате выполнения этого кода, если класс `DBConnector` — это правильно реализованный Singleton? Выберите правильный вариант.
class DBConnector {
private static $instance;
private function __construct() { /* ... */ }
public static function getInstance() { /* ... */ }
}
try {
$db = new DBConnector();
echo "Connection successful";
} catch (Error $e) {
echo "Caught error: " . $e->getMessage();
}