handbookНаписать

patterns/structural/facade/README.md · тема · 4 языка · 19 тестов

+ практика: оформление заказа

Фасад (Facade) ​

Даёт один простой вход в набор подсистем: клиент вызывает одну операцию, а порядок шагов и связи между частями знает фасад.

Проблема ​

Чтобы перевести ролик в mp4, нужны четыре подсистемы: Probe узнаёт кодек исходника, Transcoder перекодирует видео, AudioNormalizer выравнивает громкость, Muxer упаковывает результат в контейнер. Каждая простая, но вместе их надо вызвать в правильном порядке, передать MediaInfo из одной в другую и не забыть, что перекодировать уже подходящий кодек не нужно.

Если это знание живёт в каждом месте, где конвертируют видео (загрузка, админка, фоновая задача), то порядок шагов копируется трижды, а новая подсистема (скажем, водяной знак) требует правки во всех трёх.

Решение ​

Фасад VideoConverter знает подсистемы и сценарий: convert('talk.mkv', 'mp4') выбирает целевой кодек, опрашивает исходник, перекодирует только при необходимости, нормализует звук и упаковывает. Клиент получает Video и ничего не знает о шагах.

Фасад не прячет подсистемы насовсем: кому нужен только Probe, тот вызывает его напрямую. Фасад — удобный вход для типового сценария, а не стена. И он не добавляет поведения сверх композиции: вся работа делается в подсистемах, фасад только решает, кого и в каком порядке звать.

Структура ​

returns

returns

VideoConverter

+convert(path, format) : Video

Probe

+inspect(path) : MediaInfo

Transcoder

+transcode(media, codec) : MediaInfo

AudioNormalizer

+normalize(media) : MediaInfo

Muxer

+pack(media, container) : Video

MediaInfo

+path: string

+codec: string

+audioNormalized: bool

Video

+path: string

+codec: string

Подсистемы ничего не знают о фасаде и друг о друге: каждая принимает MediaInfo и возвращает новый. Вместо настоящей работы с медиа они пишут шаг в Trace: так тест видит, что и в каком порядке вызвал фасад.

Как это собирается ​

Фасад создаётся один раз, при запуске воркера очереди загрузок: VideoConverter сам собирает Probe, Transcoder, AudioNormalizer и Muxer вокруг общего Trace (в PHP это VideoConverter::create()). Воркер берёт задания из очереди и на каждое делает один вызов convert(). Какие подсистемы нужны, решает фасад: clip.mp4 уже в h264 и проходит без перекодирования, поэтому шагов у него три, а не четыре.

Задание с форматом, которого фасад не умеет, падает до запуска подсистем. Воркер ловит ошибку, помечает задание как неудачное и берёт следующее: одна плохая загрузка не останавливает очередь. Ни одного вызова подсистем в коде воркера нет.

Сценарий целиком с выводом: пример на PHP, он же на Go, Python и TypeScript в панели кода.

Реализации ​

Фасад держит две вещи: таблицу «формат -> кодек» и сценарий из четырёх вызовов. Проверка формата идёт первой, до любой подсистемы: неподдерживаемый формат не должен запустить даже опрос файла. Условие перед transcode — то знание, ради которого фасад и нужен: клиенту не надо помнить, что webm в webm перекодировать незачем.

ЯзыкОсобенность
PHPПодсистемы приходят в конструктор, VideoConverter::create($trace) собирает типовой набор; final readonly везде
GoФасад — структура с неэкспортируемыми полями-подсистемами и NewVideoConverter; ошибки — сентинелы с %w
PythonПодсистемы — dataclass, replace() вместо мутации; фасад собирает подсистемы в __init__
TypeScriptMediaInfo — readonly интерфейс, подсистемы возвращают копии через spread; фасад создаёт подсистемы в конструкторе

Запуск: make test T=patterns/structural/facade

Плюсы и минусы ​

Тесты проверяют поведение фасада через журнал шагов: mkv в mp4 проходит все четыре подсистемы по порядку; webm в webm пропускает перекодирование; неподдерживаемый формат падает, не тронув ни одной подсистемы. Последний тест — Probe работает и без фасада.

  • ✅ Клиентский код вызывает одну операцию и не зависит от состава подсистем.
  • ✅ Сценарий записан в одном месте: новый шаг (водяной знак) добавляется в фасад, а не во все места вызова.
  • ✅ Подсистемы остаются доступны напрямую для нетиповых задач.
  • ❌ Фасад легко раздувается в «божественный объект», который знает всё приложение. Держите его тонким: только порядок и связи.
  • ❌ Сценарий один на всех: кому нужен другой порядок, идёт мимо фасада, и правила снова расползаются.

Когда не применять ​

  • Подсистема одна или вызов и так простой: фасад станет обёрткой ради обёртки.
  • Нужно не упростить, а сменить интерфейс под чужой контракт: это Адаптер.
  • Клиентам нужны разные комбинации шагов: лучше дать им подсистемы и пару небольших функций-помощников, чем фасад с десятком флагов.

Отличия между языками ​

  • PHP — подсистемы приходят в конструктор, поэтому фасад легко собрать с подменами; именованный конструктор create() закрывает типовую сборку. В фреймворках ту же роль играет сервис из контейнера.
  • Go — фасадом часто служит сам пакет: экспортируется пара функций, подсистемы остаются неэкспортируемыми. Здесь подсистемы экспортированы, чтобы показать доступ в обход фасада, но поля фасада закрыты.
  • Python — модуль с функцией convert() и есть фасад; класс нужен, когда подсистемы держат общее состояние (здесь Trace). Неизменяемые MediaInfo и dataclasses.replace делают передачу между шагами безопасной.
  • TypeScript — структурная типизация позволяет отдать клиенту только тип { convert }, скрыв остальное без отдельного интерфейса. Spread { ...source, codec } даёт копию вместо мутации.

Практика ​

Учебный фасад только вызывает подсистемы по порядку, и ни одна из них не падает посреди сценария. В жизни фасад над складом, оплатой и уведомлениями отвечает ещё и за то, что делать, когда шаг не удался. Практика: оформление заказа показывает PlaceOrder, который снимает резерв, если оплата не прошла, и не валит заказ из-за упавшего уведомления.

Связанные темы ​

© 2014-2026 Тимур ТурдыевСайт и справочник работают на VPS от AdminVPS