> С какими паттернами проектирования ты работал (PHP)

Уровень: senior · Роль: backend · Язык: PHP · Категория: Технические вопросы

Компании: vanta

Стек: PHP

> Пример ответа

Короткий ответ

В основном работал с порождающими, структурными и поведенческими паттернами из GoF: Factory, Abstract Factory, Builder, Singleton, Adapter, Facade, Decorator, Proxy, Observer, Strategy, Template Method, Repository, Service Locator, Dependency Injection. В PHP-проектах чаще всего применял Repository, Factory, Strategy, Observer и DI-контейнеры. Также использовал паттерны уровня архитектуры: MVC, Middleware, Event-Driven, CQRS (частично), Unit of Work через ORM.

Подробное объяснение

Паттерны проектирования - это переиспользуемые решения типовых задач. В PHP, особенно в legacy-коде и современных фреймворках (Symfony, Laravel), они проявляются по-разному.

Порождающие паттерны - управление созданием объектов:

  • Factory Method - создание объекта через метод вместо new напрямую. Используется в Doctrine, когда сущности создаются через EntityManager.
  • Abstract Factory - семейство связанных объектов. Например, фабрика для разных типов платежных провайдеров.
  • Builder - пошаговое создание сложного объекта. Применяется для конфигураций, запросов к БД (QueryBuilder в Doctrine).
  • Singleton - гарантирует один экземпляр класса. В PHP часто критикуется, но используется для логгеров, кэш-сервисов.

Структурные паттерны - композиция классов:

  • Adapter - преобразует интерфейс одного класса к другому. Классика: обертки над сторонними API.
  • Decorator - добавляет поведение объекту без изменения его кода. В PHP часто используется для middleware-цепочки, для кэширования репозиториев.
  • Facade - упрощенный интерфейс к сложной подсистеме. Например, сервис-фасад для работы с несколькими репозиториями.
  • Proxy - ленивая загрузка, контроль доступа. Doctrine Proxy для lazy loading сущностей.

Поведенческие паттерны - взаимодействие между объектами:

  • Observer - событийная модель. В Symfony - EventDispatcher, в Laravel - Events.
  • Strategy - выбор алгоритма в рантайме. Например, разные стратегии валидации, скидок, экспорта.
  • Template Method - скелет алгоритма, переопределяемый в подклассах. Часто используется в абстрактных сервисах.
  • Repository - абстракция над хранилищем данных. Стандарт в Doctrine, Eloquent.
  • Dependency Injection - внедрение зависимостей через конструктор или сеттеры. Основа Symfony DI-контейнера.

Также на практике встречаются:

  • Middleware (PSR-15) - цепочка обработки запроса.
  • Service Locator - антипаттерн, но встречается в старых кодовых базах.
  • Unit of Work - реализован в Doctrine, отслеживает изменения сущностей.
  • Null Object - заглушка вместо null-проверок.

На практике

В реальных PHP-проектах паттерны редко применяются изолированно. Обычно они комбинируются:

  • Repository + Factory + DI - типичная связка для работы с БД.
  • Strategy + Factory - выбор алгоритма на основе конфигурации.
  • Observer + Event-Driven - для асинхронных уведомлений, аудита, логирования.
  • Decorator + Middleware - для обработки HTTP-запросов.

Важно понимать trade-off: паттерн добавляет абстракцию и сложность. В простых CRUD-проектах Repository и Factory избыточны. Я применяю паттерны осознанно, когда есть реальная потребность в расширяемости, тестируемости или переиспользовании.

Также стоит упомянуть, что в PHP-фреймворках многие паттерны уже встроены: DI-контейнер, EventDispatcher, Middleware, Repository (через ORM). Поэтому часто достаточно правильно использовать фреймворк, а не писать паттерны вручную.

Пример кода

PHP
// Пример комбинации Factory + Strategy + Repository
interface PaymentGateway
{
public function charge(float $amount): bool;
}
class StripeGateway implements PaymentGateway
{
public function charge(float $amount): bool
{
// логика Stripe
return true;
}
}
class PayPalGateway implements PaymentGateway
{
public function charge(float $amount): bool
{
// логика PayPal
return true;
}
}
class PaymentGatewayFactory
{
public function create(string $type): PaymentGateway
{
return match ($type) {
'stripe' => new StripeGateway(),
'paypal' => new PayPalGateway(),
default => throw new \InvalidArgumentException("Unknown gateway: $type"),
};
}
}
class OrderRepository
{
public function find(int $id): Order
{
// Doctrine или PDO
}
}
class PaymentService
{
public function __construct(
private PaymentGatewayFactory $factory,
private OrderRepository $repository,
) {}
public function pay(int $orderId, string $gatewayType): bool
{
$order = $this->repository->find($orderId);
$gateway = $this->factory->create($gatewayType);
return $gateway->charge($order->total);
}
}

Как отвечать на собеседовании

Начни с перечисления основных групп паттернов, затем приведи 2-3 конкретных примера из своего опыта. Обязательно объясни, почему выбрал именно этот паттерн, а не другой. Покажи понимание trade-off: когда паттерн полезен, а когда избыточен. Если просят пример кода - пиши короткий, но рабочий фрагмент, комментируя ключевые решения. Не перечисляй все паттерны подряд без контекста - это выглядит как заучивание. Лучше глубоко разобрать 3-4 паттерна, чем поверхностно упомянуть 15. Также упомяни, как паттерны реализованы во фреймворках, которыми пользуешься.

Что проверяет интервьюер

Интервьюер оценивает:

  • понимание назначения паттернов, а не только названия;
  • способность применить паттерн к реальной задаче;
  • осознание альтернатив и trade-off;
  • знание, как паттерны встроены в PHP-фреймворки;
  • умение объяснить код и обосновать выбор.

На senior-уровне важно показать, что ты не используешь паттерны ради паттернов, а решаешь конкретные проблемы: тестируемость, расширяемость, снижение связанности. Также ценится умение критиковать паттерны: например, Singleton - антипаттерн в большинстве случаев, Service Locator - скрытая зависимость.

Типичные ошибки

  • Перечисление всех паттернов без примеров - звучит как шпаргалка.
  • Применение паттернов там, где они не нужны - оверинжиниринг.
  • Путаница между похожими паттернами: Factory vs Abstract Factory, Strategy vs Template Method, Decorator vs Proxy.
  • Непонимание, как паттерн реализован в конкретном фреймворке.
  • Игнорирование альтернатив: например, вместо Observer можно использовать простой callback или шину событий.
  • Утверждение, что Singleton - хороший паттерн, без оговорок о его проблемах с тестированием и состоянием.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью