Вы когда-нибудь писали код, а через месяц не могли понять, что там происходит? Или добавляли новую фичу, и всё ломалось? Знакомо. Шаблоны проектирования — это готовые рецепты для типовых задач. Они помогают делать код понятным, гибким и удобным для изменений. В этой статье мы разберём самые популярные шаблоны в PHP на живых примерах.
Что такое шаблоны проектирования и зачем они нужны
Шаблон проектирования (design pattern) — это проверенное решение частой проблемы в программировании. Представьте, что вы строите дом. Вместо того чтобы каждый раз придумывать, как сделать крышу, вы используете готовую схему. Так и тут. Шаблоны делятся на три группы: порождающие (как создавать объекты), структурные (как собирать классы вместе) и поведенческие (как объекты общаются друг с другом).
В PHP шаблоны особенно полезны, потому что язык динамический и часто используется в больших проектах. Они помогают избежать хаоса, когда код разрастается.
Порождающие шаблоны: Singleton, Factory и Builder
Начнём с порождающих. Самый известный — Singleton (одиночка). Он гарантирует, что у класса будет только один экземпляр. Например, подключение к базе данных не должно создаваться каждый раз заново.
class Database {
private static ?Database $instance = null;
private PDO $pdo;
private function __construct() {
$this->pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
}
public static function getInstance(): Database {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function getConnection(): PDO {
return $this->pdo;
}
}
Но будьте осторожны. Singleton часто критикуют за то, что он создаёт глобальное состояние. Используйте его только там, где это действительно нужно, например, для логгера или конфигурации.
Другой полезный шаблон — Factory (фабрика). Он прячет логику создания объектов. Допустим, у вас есть разные типы уведомлений: email, SMS, push. Фабрика решит, какой объект создать.
interface Notification {
public function send(string $message): void;
}
class EmailNotification implements Notification {
public function send(string $message): void {
echo "Email sent: $message";
}
}
class SMSNotification implements Notification {
public function send(string $message): void {
echo "SMS sent: $message";
}
}
class NotificationFactory {
public static function create(string $type): Notification {
return match ($type) {
'email' => new EmailNotification(),
'sms' => new SMSNotification(),
default => throw new InvalidArgumentException("Unknown type")
};
}
}
Builder (строитель) пригодится, когда объект сложный и состоит из множества частей. Например, создание отчёта с разными секциями.
Структурные шаблоны: Adapter и Decorator
Структурные шаблоны помогают организовать классы так, чтобы они работали вместе. Adapter (адаптер) — это как переходник для розетки. Он позволяет использовать несовместимые интерфейсы.
Представьте, у вас есть старая библиотека для работы с XML, а новая система ожидает JSON. Адаптер преобразует одно в другое.
class XmlData {
public function getXml(): string {
return '- value
';
}
}
interface JsonDataInterface {
public function getJson(): string;
}
class XmlToJsonAdapter implements JsonDataInterface {
private XmlData $xmlData;
public function __construct(XmlData $xmlData) {
$this->xmlData = $xmlData;
}
public function getJson(): string {
$xml = simplexml_load_string($this->xmlData->getXml());
return json_encode($xml);
}
}
Decorator (декоратор) позволяет динамически добавлять функциональность объекту. Как обёртка для подарка — вы добавляете слои, не меняя сам подарок.
interface Coffee {
public function getCost(): float;
public function getDescription(): string;
}
class SimpleCoffee implements Coffee {
public function getCost(): float {
return 10;
}
public function getDescription(): string {
return 'Simple coffee';
}
}
class MilkDecorator implements Coffee {
private Coffee $coffee;
public function __construct(Coffee $coffee) {
$this->coffee = $coffee;
}
public function getCost(): float {
return $this->coffee->getCost() + 2;
}
public function getDescription(): string {
return $this->coffee->getDescription() . ', with milk';
}
}
Поведенческие шаблоны: Strategy и Observer
Поведенческие шаблоны управляют взаимодействием между объектами. Strategy (стратегия) позволяет менять алгоритм на лету. Например, расчёт стоимости доставки в зависимости от способа.
interface ShippingStrategy {
public function calculate(float $weight): float;
}
class ExpressShipping implements ShippingStrategy {
public function calculate(float $weight): float {
return $weight * 10;
}
}
class StandardShipping implements ShippingStrategy {
public function calculate(float $weight): float {
return $weight * 5;
}
}
class Order {
private ShippingStrategy $strategy;
public function setShippingStrategy(ShippingStrategy $strategy): void {
$this->strategy = $strategy;
}
public function getShippingCost(float $weight): float {
return $this->strategy->calculate($weight);
}
}
Observer (наблюдатель) — это когда один объект уведомляет других об изменениях. Как подписка на новости. В PHP есть встроенный SplObserver и SplSubject.
class User implements \SplSubject {
private \SplObjectStorage $observers;
private string $email;
public function __construct() {
$this->observers = new \SplObjectStorage();
}
public function attach(\SplObserver $observer): void {
$this->observers->attach($observer);
}
public function detach(\SplObserver $observer): void {
$this->observers->detach($observer);
}
public function notify(): void {
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
public function setEmail(string $email): void {
$this->email = $email;
$this->notify();
}
}
Сравнение популярных шаблонов
| Шаблон | Тип | Когда использовать | Пример |
|---|---|---|---|
| Singleton | Порождающий | Нужен только один экземпляр | Подключение к БД |
| Factory | Порождающий | Создание объектов по условию | Уведомления |
| Adapter | Структурный | Несовместимые интерфейсы | XML в JSON |
| Decorator | Структурный | Добавление функциональности без наследования | Кофе с добавками |
| Strategy | Поведенческий | Смена алгоритма на лету | Стоимость доставки |
| Observer | Поведенческий | Уведомление об изменениях | Подписка на события |
Заключение
Шаблоны проектирования — это не магия, а инструменты. Они помогают писать код, который легко читать, тестировать и менять. Начните с малого: примените Factory для создания объектов или Decorator для расширения функциональности. Со временем вы научитесь видеть, где какой шаблон подходит. Главное — не переусердствуйте. Иногда простой if работает лучше, чем сложная архитектура.
Помните: хороший код — это код, который понятен другим разработчикам (и вам через полгода). Шаблоны в этом отличные помощники.



