Паттерны проектирования

33 вопросов

1 Чем отличаются порождающие, структурные и поведенческие паттерны?

Порождающие (Creational) - создание объектов: Singleton, Factory, Abstract Factory, Builder, Prototype. Решают проблему инстанцирования.

Структурные (Structural) - композиция классов и объектов: Adapter, Decorator, Facade, Bridge, Composite, Proxy, Flyweight. Упрощают связи между сущностями.

Поведенческие (Behavioral) - распределение ответственности и взаимодействие: Observer, Strategy, Command, State, Template Method, Chain of Responsibility, Mediator, Visitor, Iterator, Memento. Описывают алгоритмы и потоки данных.

Открыть отдельно →
2 Что такое Singleton? Когда применять?

Паттерн гарантирует единственный экземпляр класса в приложении.

class Config {
    private static ?self $instance = null;
    private function __construct() {}
    public static function getInstance(): self {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}

Применять для: глобального конфига, логгера, подключения к БД. Минусы: усложняет тестирование (сложно подменить), скрытая глобальная зависимость. В PHP часто предпочтительнее DI-контейнер.

Открыть отдельно →
3 Что такое Factory и Abstract Factory?

Factory Method - метод класса, создающий объекты (делегирует создание подклассам).

interface LoggerFactory {
    public function createLogger(): Logger;
}
class FileLoggerFactory implements LoggerFactory {
    public function createLogger(): Logger { return new FileLogger(); }
}

Abstract Factory - фабрика фабрик: создает семейства связанных объектов (например, UI-виджеты для Windows и Mac). Один интерфейс, несколько реализаций для разных "семейств".

Открыть отдельно →
4 Что такое Builder?

Пошаговое создание сложного объекта через отдельный класс Builder. Удобно при многих опциональных параметрах.

$query = (new QueryBuilder())
    ->select(['id', 'name'])
    ->from('users')
    ->where('active', true)
    ->orderBy('name')
    ->limit(10)
    ->build();

Избегает "телескопических" конструкторов с десятками параметров. Director (опционально) задает порядок шагов.

Открыть отдельно →
5 Что такое Prototype?

Создание новых объектов через клонирование существующего (прототипа), а не через конструктор.

class Document implements Cloneable {
    public function __clone() {
        $this->sections = array_map(fn($s) => clone $s, $this->sections);
    }
}
$doc2 = clone $doc1;

Полезно когда создание объекта дорого (сложная инициализация, запросы к БД) или когда нужно копировать с небольшими изменениями.

Открыть отдельно →
6 Что такое Adapter?

Структурный паттерн: преобразует интерфейс одного класса в другой, ожидаемый клиентом. Позволяет использовать несовместимые интерфейсы вместе.

class StripeAdapter implements PaymentGateway {
    public function __construct(private StripeClient $stripe) {}
    public function charge(float $amount, string $token): void {
        $this->stripe->charges->create(['amount' => $amount * 100, 'source' => $token]);
    }
}

Объектный адаптер делегирует вызовы обернутому объекту; классовый - наследует целевой класс (в PHP реже).

Открыть отдельно →
7 Что такое Decorator?

Динамически добавляет объекту новое поведение, оборачивая его в объект-декоратор с тем же интерфейсом.

$handler = new LoggingDecorator(
    new CacheDecorator(
        new DatabaseHandler()
    )
);

Альтернатива наследованию для расширения функциональности. В PHP часто через обертки над PSR-интерфейсами (Logger, Cache, HttpClient).

Открыть отдельно →
8 Что такое Facade?

Упрощенный единый интерфейс к подсистеме (набору классов). Скрывает сложность за простым API.

class OrderFacade {
    public function __construct(
        private OrderRepository $repo,
        private PaymentService $payment,
        private Notifier $notifier
    ) {}
    public function placeOrder(Cart $cart): Order {
        $order = $this->repo->create($cart);
        $this->payment->charge($order);
        $this->notifier->send($order);
        return $order;
    }
}

Клиент вызывает один метод вместо координации нескольких сервисов.

Открыть отдельно →
9 Что такое Bridge?

Разделяет абстракцию и реализацию так, чтобы они могли меняться независимо. Вместо множества подклассов (Shape+Color комбинации) - две иерархии: абстракция и реализация.

interface Renderer { public function renderCircle(float $r): string; }
class VectorRenderer implements Renderer { ... }
class RasterRenderer implements Renderer { ... }
abstract class Shape {
    public function __construct(protected Renderer $renderer) {}
}
class Circle extends Shape { ... }

Новые формы и новые способы отрисовки добавляются без взрывного роста классов.

Открыть отдельно →
10 Что такое Composite?

Древовидная структура: клиент работает с листьями и составными узлами единообразно через общий интерфейс.

interface Component { public function render(): string; }
class Leaf implements Component { public function render(): string { return "Leaf"; } }
class Composite implements Component {
    private array $children = [];
    public function add(Component $c): void { $this->children[] = $c; }
    public function render(): string {
        return implode('', array_map(fn($c) => $c->render(), $this->children));
    }
}

Примеры: дерево DOM, меню с подменю, файловая система.

Открыть отдельно →
11 Что такое Proxy?

Объект-заместитель контролирует доступ к другому объекту: ленивая инициализация, кеширование, проверка прав, логирование.

class LazyRepositoryProxy implements UserRepositoryInterface {
    private ?UserRepository $real = null;
    public function find(int $id): User {
        $this->real ??= new UserRepository($this->connection);
        return $this->real->find($id);
    }
}

Виды: Virtual (ленивая загрузка), Protection (access control), Remote, Cache proxy.

Открыть отдельно →
12 Что такое Flyweight?

Разделение общего состояния между множеством объектов для экономии памяти. Общее состояние выносится в один объект (flyweight), уникальное хранится снаружи.

class TreeType {
    public function __construct(private string $name, private string $color) {}
}
class TreeFactory {
    private array $types = [];
    public function getType(string $name, string $color): TreeType {
        $key = $name . '-' . $color;
        return $this->types[$key] ??= new TreeType($name, $color);
    }
}

Применение: символы в текстовом редакторе, деревья в игре, повторяющиеся иконки.

Открыть отдельно →
13 Что такое Observer?

Паттерн подписки: объект (Subject) хранит список наблюдателей и уведомляет их об изменениях.

interface Observer { public function update(Subject $subject): void; }
class Subject {
    private array $observers = [];
    public function attach(Observer $o): void { $this->observers[] = $o; }
    public function notify(): void {
        foreach ($this->observers as $o) { $o->update($this); }
    }
}

В PHP: Symfony EventDispatcher, Laravel Events. Слабая связь между издателем и подписчиками.

Открыть отдельно →
14 Что такое Strategy?

Семейство взаимозаменяемых алгоритмов, инкапсулированных в отдельные классы. Выбор стратегии в рантайме.

interface SortStrategy { public function sort(array $data): array; }
class QuickSort implements SortStrategy { ... }
class Context {
    public function __construct(private SortStrategy $strategy) {}
    public function setStrategy(SortStrategy $s): void { $this->strategy = $s; }
    public function execute(array $data): array { return $this->strategy->sort($data); }
}

Избегает условных веток по типу алгоритма. Часто через внедрение стратегии в конструктор (DI).

Открыть отдельно →
15 Что такое Command?

Инкапсулирует запрос как объект: действие + параметры. Позволяет ставить операции в очередь, отменять, логировать.

interface Command { public function execute(): void; }
class CreateOrderCommand implements Command {
    public function __construct(private OrderService $service, private OrderDTO $dto) {}
    public function execute(): void { $this->service->create($this->dto); }
}
$bus->dispatch(new CreateOrderCommand($service, $dto));

Используется в очередях (Laravel Jobs, Symfony Messenger), Undo/Redo, макросах.

Открыть отдельно →
16 Что такое State?

Поведение объекта зависит от внутреннего состояния; объект меняет класс поведения при смене состояния.

interface State { public function proceed(Order $order): void; }
class DraftState implements State { public function proceed(Order $o): void { $o->setState(new ConfirmedState()); } }
class Order {
    private State $state;
    public function proceed(): void { $this->state->proceed($this); }
}

Избегает больших switch/if по состоянию. Переходы инкапсулированы в классах состояний.

Открыть отдельно →
17 Что такое Template Method?

Базовый класс задает скелет алгоритма (шаги), подклассы переопределяют отдельные шаги.

abstract class DataImporter {
    final public function import(string $path): void {
        $data = $this->read($path);
        $normalized = $this->normalize($data);
        $this->persist($normalized);
    }
    abstract protected function read(string $path): array;
    abstract protected function normalize(array $data): array;
    protected function persist(array $data): void { /* default */ }
}

Переиспользование общей логики без дублирования. Хук-методы - опциональные переопределяемые шаги.

Открыть отдельно →
18 Что такое Chain of Responsibility?

Запрос передается по цепочке обработчиков; каждый решает, обработать самому или передать дальше.

interface Handler {
    public function setNext(Handler $h): Handler;
    public function handle(Request $request): ?Response;
}
class AuthMiddleware implements Handler {
    private ?Handler $next = null;
    public function setNext(Handler $h): Handler { $this->next = $h; return $h; }
    public function handle(Request $r): ?Response {
        if (!$this->auth->check($r)) return $this->unauthorized();
        return $this->next?->handle($r);
    }
}

Примеры: middleware, валидация по цепочке, логирование/кэш по цепочке.

Открыть отдельно →
19 Что такое Mediator?

Объект-посредник инкапсулирует взаимодействие множества объектов. Компоненты не ссылаются друг на друга, только на медиатор.

class ChatRoom {
    public function send(User $from, string $msg): void {
        foreach ($this->users as $user) {
            if ($user !== $from) $user->receive($from, $msg);
        }
    }
}

Снижает связанность между коллегами. Минус: медиатор может разрастись. Примеры: диалоги в UI, оркестрация сервисов.

Открыть отдельно →
20 Что такое Visitor?

Добавляет новые операции к иерархии классов без изменения самих классов. Двойная диспетчеризация: элемент принимает посетителя, посетитель вызывает метод для конкретного типа элемента.

interface Visitor { function visitA(ElementA $e): void; function visitB(ElementB $e): void; }
interface Element { function accept(Visitor $v): void; }
class ElementA implements Element {
    public function accept(Visitor $v): void { $v->visitA($this); }
}

Применение: экспорт в разные форматы (JSON, XML), подсчет статистики по AST. Минус: добавление нового типа элемента требует менять всех посетителей.

Открыть отдельно →
21 Что такое Iterator?

Доступ к элементам коллекции последовательно без раскрытия внутренней структуры.

class Collection implements IteratorAggregate {
    public function getIterator(): Iterator {
        return new ArrayIterator($this->items);
    }
}
foreach ($collection as $item) { ... }

В PHP: Iterator, IteratorAggregate, Generator (yield). Позволяет ленивую итерацию и единый способ обхода разных структур.

Открыть отдельно →
22 Что такое Memento?

Сохранение и восстановление внутреннего состояния объекта без нарушения инкапсуляции. Memento - объект-снимок состояния; Originator создает и восстанавливается из снимка; Caretaker хранит снимки.

class Memento { public function __construct(private string $state) {}
    public function getState(): string { return $this->state; }
}
$originator->save(); // создает Memento
$caretaker->add($originator->saveToMemento());
$originator->restore($caretaker->get(0));

Используется для Undo, снапшотов, отката конфигурации.

Открыть отдельно →
23 Что такое паттерн Specification?

Инкапсулирует бизнес-правило в объект с методом isSatisfiedBy(candidate). Спецификации можно комбинировать (and, or, not).

interface Specification { public function isSatisfiedBy($candidate): bool; }
class ActiveUserSpec implements Specification {
    public function isSatisfiedBy($user): bool { return $user->isActive(); }
}
$spec = new ActiveUserSpec()->and(new PremiumSpec());

Удобно для фильтрации репозиториев, валидации доменных правил, переиспользуемых условий.

Открыть отдельно →
24 Что такое Fluent Interface?

Стиль API, при котором вызовы методов можно цепочкой (каждый метод возвращает объект для следующего вызова).

$query->select('id', 'name')
    ->from('users')
    ->where('active', 1)
    ->orderBy('name')
    ->limit(10);

Улучшает читаемость. Реализация: методы возвращают $this или новый билдер. Не путать с паттерном Builder - fluent может быть частью любого API.

Открыть отдельно →
25 Что такое Multiton?

Обобщение Singleton: ограниченное множество именованных экземпляров (по ключу).

class Multiton {
    private static array $instances = [];
    public static function getInstance(string $key): self {
        if (!isset(self::$instances[$key])) {
            self::$instances[$key] = new self($key);
        }
        return self::$instances[$key];
    }
}

Пример: соединения к разным БД по имени. В современном PHP чаще решают через DI и именованные сервисы.

Открыть отдельно →
26 Что такое паттерн Repository?

Абстракция доступа к данным: коллекция доменных объектов в памяти. Клиент работает с сущностями, не зная о SQL/хранилище.

interface UserRepositoryInterface {
    public function find(int $id): ?User;
    public function save(User $user): void;
    public function findByEmail(string $email): ?User;
}
class EloquentUserRepository implements UserRepositoryInterface {
    public function find(int $id): ?User { return User::find($id); }
    // ...
}

Плюсы: тестируемость (подмена на in-memory), смена хранилища, единая точка доступа к данным.

Открыть отдельно →
27 Что такое Service Locator?

Глобальный реестр, по запросу возвращающий сервисы. Клиент запрашивает зависимость по имени/типу.

$logger = $locator->get(LoggerInterface::class);
$db = $locator->get('db');

Минусы: скрытые зависимости, сложнее тесты, код привязан к локатору. Предпочтительнее Dependency Injection: зависимости передаются явно (конструктор/сеттер).

Открыть отдельно →
28 Что такое IoC (Inversion of Control)?

Принцип: управление потоком и созданием зависимостей передается внешней среде (фреймворку, контейнеру), а не самому коду.

Вместо "класс сам создает зависимости" - "зависимости передаются извне". Конкретизация IoC - DI (Dependency Injection): внедрение зависимостей через конструктор, метод или свойство.

Плюсы: слабая связанность, тестируемость, гибкая замена реализаций.

Открыть отдельно →
29 Что такое DI-контейнер?

Контейнер знает, как создавать объекты и разрешать их зависимости (по типам, именам, тегам). При запросе сервиса контейнер создает его и все зависимости рекурсивно.

// Регистрация
$container->bind(LoggerInterface::class, FileLogger::class);
$container->singleton(Connection::class, fn() => new PdoConnection(...));

// Резолв
$logger = $container->make(LoggerInterface::class);

В PHP: Laravel Container, Symfony DI, PHP-DI. Поддерживают autowiring, теги, фабрики.

Открыть отдельно →
30 Что такое MVC? Роли компонентов.

Model - данные и бизнес-логика (домен, репозитории, сервисы). View - представление (шаблоны, отображение). Controller - принимает запрос, вызывает модель, выбирает view и отдает ответ.

Контроллер не должен содержать бизнес-логику - только оркестрацию. Толстая модель, тонкий контроллер. В вебе: Front Controller (один entry point) + роутинг к конкретному контроллеру.

Открыть отдельно →
31 Как разделять ответственность: Controller, Service, Repository?

Controller - HTTP: валидация ввода, вызов сервиса, формирование ответа (JSON/redirect). Без бизнес-логики.

Service - слой приложения: оркестрация репозиториев и домена, транзакции, события. Содержит use-case логику.

Repository - доступ к данным: find, save, выборки. Абстракция над БД/API. Возвращает сущности или коллекции.

Запрос: Controller -> Service -> Repository -> DB. Модель (Entity) - доменный объект; репозиторий работает с моделями.

Открыть отдельно →
32 Active Record vs Data Mapper?

Active Record: объект инкапсулирует и данные, и персистентность. Методы save(), delete(), статические find(). Пример: Laravel Eloquent. Простота, быстрый старт; минус - смешение домена и доступа к БД.

Data Mapper: сущности "глупые", маппер отдельно переносит данные в БД и обратно. Пример: Doctrine. Чистый домен, тестируемость; минус - больше кода и сложность.

Открыть отдельно →
33 Какие антипаттерны знаете?
  • God Object - класс делает слишком много.
  • Spaghetti Code - нет структуры, все вперемешку.
  • Copy-Paste - дублирование вместо абстракции.
  • Singleton overuse - глобальное состояние, сложные тесты.
  • Anemic Model - модель только данные, логика в сервисах (иногда приемлемо, но не DDD).
  • Circular dependency - A зависит от B, B от A.
  • Premature optimization - оптимизация без измерений.
Открыть отдельно →
🧠Квиз 🏆Лидеры 🎯Собесед. 📖Вопросы 📚База зн.