Один з інструментів DRY або єдиний дозволений copy/paste

Один з інструментів DRY або єдиний дозволений copy/paste

Багато технічних співбесід показують, що розробники або не використовують трейти зовсім, або роблять це без розуміння практичного сенсу. Часто їх просто бояться — мовляв, поведінка трейту може бути неочевидною або ж спричинити баги, які складно відслідкувати.

Але суть в тому, що трейти — це всього лише інструмент, який треба застосовувати там, де від нього буде найбільше користі.

І найчастіше це не сервісні класи (хоча є ситуації, коли і тут вони будуть доречні), а обʼєктні структури: сутності, DTO, VO, event'и тощо. Саме там трейти — ідеальний інструмент для роздачі типової поведінки без зайвої ієрархії і порушення DRY.

Гарний приклад — бібліотека knplabs/doctrine-behaviors. Своїми інтерфейсами вона надає вашим сутностям типову поведінку (наприклад, Timestampable, SoftDeletable, Translatable тощо), але замість того, щоб реалізовувати інтерфейси в кожному класі, ви просто додаєте реалізацію — підключаючи відповідний трейт. Ніяких милиць у вигляді абстрактних батьківських класів, які «надають поведінку», тільки чистий код, лаконічний use TraitName і ніякого copy/paste.

Хоча справедливості заради варто визнати: copy/paste все одно відбувається, просто не в коді, а на рівні інтерпретатора.

Висновок

Трейти — це спосіб дотримуватися DRY і не лізти в спадкування там, де воно недоречне. Це дуже потужний інструмент, якщо використовувати його правильно і там, де треба.

Порада: як зробити, щоб трейт гарантовано працював в будь-якому класі без помилок?

Ніколи не звертайтесь всередині трейту до властивостей або методів класу, в який трейт підключено. Трейт не знає, куди його вставлять — і тому контекст будь-якого трейту має бути самообмеженим. Це головна умова, щоб ваш трейт працював стабільно і не викликав помилки.

Погано:

trait BadTrait {
    public function generateUniqueId(): void 
    {
        $this->id = uniqid();
    }
}

Добре:

trait GoodTrait {
    protected string $id; 

    public function generateUniqueId(): void 
    {
        $this->id = uniqid();
    }
}

Вперше опубліковано в Telegram-каналі «Створений щоб помирати» 26.06.2025.