Представь, что ты строишь дом. Можно каждый раз класть кирпичи по-новому, придумывая свои способы. А можно использовать проверенные решения: фундамент, стены, крыша. В программировании то же самое. Паттерны проектирования — это готовые чертежи для типовых задач. Они экономят время, нервы и делают код понятным для других.
В этой статье мы разберём три самых востребованных паттерна в PHP: Singleton, Factory и Observer. Никакой магии, только практика. Поехали.
Singleton: когда объект должен быть один
Singleton (одиночка) — это паттерн, который гарантирует, что у класса есть только один экземпляр. И к этому экземпляру есть глобальная точка доступа. Зачем? Например, для подключения к базе данных. Не нужно создавать новое соединение каждый раз — это дорого и медленно.
В PHP Singleton реализуется так:
<?php
class Database {
private static $instance = null;
private $connection;
// Приватный конструктор — нельзя создать через new
private function __construct() {
$this->connection = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
}
// Статический метод для получения экземпляра
public static function getInstance(): self {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function getConnection(): PDO {
return $this->connection;
}
}
// Использование
$db = Database::getInstance();
$conn = $db->getConnection();
?>Обрати внимание: конструктор приватный. Снаружи нельзя написать new Database(). Только через getInstance(). Это и есть суть.
Factory: делегируем создание объектов
Factory (фабрика) — это паттерн, который выносит логику создания объектов в отдельный класс. Представь, что ты пишешь интернет-магазин. Есть разные типы товаров: книги, электроника, одежда. Каждый тип требует своей логики при создании. Фабрика берёт это на себя.
Пример простой фабрики:
<?php
interface Product {
public function getName(): string;
}
class Book implements Product {
public function getName(): string {
return 'Книга';
}
}
class Electronics implements Product {
public function getName(): string {
return 'Электроника';
}
}
class ProductFactory {
public static function create(string $type): Product {
return match($type) {
'book' => new Book(),
'electronics' => new Electronics(),
default => throw new InvalidArgumentException('Неизвестный тип товара'),
};
}
}
// Использование
$product = ProductFactory::create('book');
echo $product->getName(); // Выведет: Книга
?>Зачем это нужно? Если завтра добавится новый тип товара (например, «Мебель»), ты меняешь только фабрику, а не весь код, где создаются объекты. Меньше правок — меньше багов.
| Паттерн | Когда использовать |
|---|---|
| Simple Factory | Когда создание объекта простое, но хочешь изолировать логику |
| Factory Method | Когда подклассы должны сами решать, какой объект создавать |
| Abstract Factory | Когда нужно создавать семейства связанных объектов (например, для разных тем оформления) |
new в разных местах — это повод задуматься о фабрике. Она централизует создание и упрощает тестирование.Observer: уведомляем подписчиков
Observer (наблюдатель) — это паттерн, где один объект (субъект) уведомляет множество других (наблюдателей) об изменениях своего состояния. Классический пример: система событий. Когда пользователь регистрируется, нужно отправить письмо, записать в лог, обновить статистику. Вместо того чтобы писать всё в одном месте, используем наблюдателей.
Реализация в PHP:
<?php
interface Observer {
public function update(string $event): void;
}
class User {
private array $observers = [];
public function attach(Observer $observer): void {
$this->observers[] = $observer;
}
public function register(): void {
echo 'Пользователь зарегистрирован.\n';
$this->notify('user.registered');
}
private function notify(string $event): void {
foreach ($this->observers as $observer) {
$observer->update($event);
}
}
}
class EmailNotifier implements Observer {
public function update(string $event): void {
if ($event === 'user.registered') {
echo 'Отправляем приветственное письмо.\n';
}
}
}
class Logger implements Observer {
public function update(string $event): void {
echo 'Записываем в лог: ' . $event . '\n';
}
}
// Использование
$user = new User();
$user->attach(new EmailNotifier());
$user->attach(new Logger());
$user->register();
?>Вывод:
Пользователь зарегистрирован.
Отправляем приветственное письмо.
Записываем в лог: user.registered.Класс User не знает, кто его слушает. Можно добавлять новых наблюдателей без изменения самого пользователя. Это принцип открытости/закрытости в действии.
Советы по выбору паттерна
Не пытайся использовать паттерны везде. Они — инструмент, а не цель. Вот простые критерии:
- Singleton — если ресурс действительно глобальный и единственный (БД, логгер).
- Factory — если создание объекта сложное или зависит от условий.
- Observer — если нужно, чтобы одно событие вызывало несколько реакций, и список реакций может меняться.
Помни: плохой код с паттернами остаётся плохим кодом. Сначала пиши просто, потом рефактори.
SplSubject и SplObserver. Они уже содержат базовую логику. Используй их, чтобы не писать велосипед.Резюме
Паттерны проектирования — это не магия, а проверенные временем решения. Singleton помогает управлять единственным экземпляром. Factory избавляет от размазанного по коду создания объектов. Observer делает систему событий гибкой.
Главное — не злоупотребляй. Паттерн должен решать проблему, а не создавать новую. Начни с малого: посмотри на свой код, найди места, где ты часто пишешь new или где одно изменение тянет за собой правки в десяти файлах. Примени паттерн — и почувствуешь разницу.
Студия WNDER



