Начинайте с запрещённого состояния
Класс нужен не потому, что проект стал длиннее ста строк. Он оправдан, когда группа значений живёт по общему правилу. Денежная сумма не должна смешивать валюты, заказ не может перескочить из cancelled в paid, email обязан быть нормализован один раз. Конструктор или именованный фабричный метод создаёт только допустимый экземпляр.
Публичные set-методы для каждого поля возвращают объект к роли пассивного контейнера. Лучше выразить операции предметными глаголами: confirm(), cancel(), rename().
Value object и сущность
Value object определяется содержимым и обычно неизменяем: два Email с одинаковым нормализованным адресом эквивалентны. Сущность сохраняет идентичность во времени, даже если меняет состояние. Это различие помогает решить, нужен ли id, где сравнивать объекты и как сохранять их в базе.
readonly уменьшает поверхность случайных изменений, но не делает вложенные объекты неизменяемыми автоматически. Инвариант всё равно принадлежит вашему коду.
Композиция как схема зависимостей
Интерфейс в правильном месте
Создавайте интерфейс там, где приложению нужна заменяемая граница: хранилище, часы, генератор идентификаторов, отправка уведомления. Интерфейс с одним production-исполнителем всё равно полезен, если unit-тесту нужна детерминированная реализация. Но интерфейс над каждым классом только увеличивает число файлов.
Сценарий формулирует нужные ему методы, а не копирует весь API PDO или внешнего SDK. Такой узкий контракт переживает смену инфраструктуры легче.
Возможности новых веток PHP
PHP 8.4 добавил property hooks и расширил способы описывать доступ к свойствам. PHP 8.5 принёс pipe operator, #[NoDiscard] и другие улучшения типобезопасности. Эти возможности полезны, но не должны быть условием понимания модели. Сначала выразите правило обычным методом, затем применяйте новый синтаксис, если он делает контракт яснее и поддерживаемая версия его допускает.
При миграции проверяйте не только новые конструкции, но и несовместимости и deprecations. Учебный пример не обязан демонстрировать каждую новинку в одном классе.
Composition root
Создание PDO, репозиториев, сервисов и контроллеров сосредоточьте в одном месте запуска. Остальной код получает зависимости через конструктор. Service locator и глобальный контейнер скрывают фактический вход класса и делают тест зависимым от порядка инициализации.
Фреймворк может автоматизировать связывание, но мысленная модель остаётся прежней: объект не должен сам искать инфраструктуру.
Практика главы
Смоделируйте задачу со статусами new, active и done. Запретите завершение уже завершённой задачи, вынесите время в Clock и сохранение — в TaskRepository.
Закрепите материал
Отметка, заметка, чек-лист и ответы остаются в localStorage этого браузера и не отправляются в аналитику.
Перелинковка со справочником
Проверка актуальности
- php.nethttps://www.php.net/releases/8.5/en.phpПервичный источник для актуализации фактов. Адрес приведён как текст и не является активной ссылкой.
- php.nethttps://www.php.net/manual/en/migration85.phpПервичный источник для актуализации фактов. Адрес приведён как текст и не является активной ссылкой.
- php-fig.orghttps://www.php-fig.org/psr/psr-4/Первичный источник для актуализации фактов. Адрес приведён как текст и не является активной ссылкой.
Текст и схемы созданы для этого самоучителя без воспроизведения страниц, иллюстраций и листингов приложенной книги. Проверено: .