Самоучитель: PHP 8.5: инженерный практикум
Глава 14 из 22IV. Объектный дизайн100 минут

Глава 14. Композиция, интерфейсы и осторожное наследование

Связь has-a обычно меняется легче, чем is-a: объект получает нужное поведение через маленький контракт, а не наследует случайные методы предка.

После главы вы сможете
  • выбрать композицию
  • сформулировать потребительский интерфейс
  • ограничить trait

Класс начинается с ответственности

Workout отвечает за предметные переходы, VolumeCalculator вычисляет метрику, WorkoutRepository сохраняет и загружает, Clock сообщает время. Один класс не должен одновременно знать SQL, HTML и правила публикации.

Количество классов не является целью. Разделение оправдано, если компоненты меняются по разным причинам, требуют разных тестов или представляют независимые понятия языка продукта.

Композиция зависимостей

Сценарий публикацииHandler зависит от узких контрактов и связывает решение без наследования инфраструктуры.HandlerorchestrateRepositoryload/savePolicyauthorizeClocktimeLoggerobserve
Handler зависит от узких контрактов и связывает решение без наследования инфраструктуры.

Интерфейс принадлежит потребителю

PublishWorkoutHandler требует load, save и nextVersion — ему не нужен универсальный repository с двадцатью методами. Маленький интерфейс показывает реальные зависимости и облегчает тестовую замену.

Не создавайте интерфейс для каждого класса механически. Он полезен на границе вариативности: хранилище, часы, очередь, logger. Внутренний value object может оставаться конкретным final-классом.

Наследование требует подстановки

Подкласс должен работать везде, где ожидается базовый тип, не усиливая предусловия и не ломая смысл методов. Если StrengthWorkout запрещает операцию, разрешённую Workout, и бросает неожиданное исключение, иерархия выбрана неверно.

Чаще тип тренировки является enum или отдельной стратегией расчёта, переданной через композицию. Наследование удобно для стабильного общего протокола, а не для экономии нескольких строк.

Trait — локальное включение кода

Trait может объединить небольшой технический фрагмент без собственной идентичности, например нормализацию correlation id. Он не создаёт независимый контракт и способен скрыть зависимости через методы принимающего класса.

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

Лабораторная работа

Разделите публикацию тренировки на Handler, Repository, AccessPolicy и Clock. Напишите интерфейсы только со стороны Handler и сравните тест с вариантом, где всё наследуется от BaseController.

Локальный прогресс

Закрепите материал

Отметка, заметка, чек-лист и ответы остаются в localStorage этого браузера и не отправляются в аналитику.

Перелинковка со справочником

Проверка актуальности

  • php-fig.orghttps://www.php-fig.org/psr/psr-3/Первичный источник для актуализации фактов. Адрес приведён как текст и не является активной ссылкой.
  • php-fig.orghttps://www.php-fig.org/psr/psr-20/Первичный источник для актуализации фактов. Адрес приведён как текст и не является активной ссылкой.

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

Данные этого сервиса

Настройки cookie и локальных данных

Аналитика

Текущий статус: не выбран. До согласия обе аналитические системы не загружаются.

Локальные данные

Удаление затрагивает только ключи PHP-сервиса в этом браузере и требует подтверждения.

Политики

Политика конфиденциальности · Политика использования файлов cookie