Перейти до основного вмісту

Don't Repeat Yourself (DRY)

· 2 хв. читання
Petro Ostapuk
Software Engineer

У програмуванні існує принцип DRY, або "не повторюйся" (Don't Repeat Yourself). Як правило, якщо ви повторюєте частини своєї логіки, у вас буде більше місць, які потрібно буде виправити, якщо буде виявлено проблему, і ймовірність появи невідповідностей у вашому додатку, оскільки майбутні вимоги збільшують складність додатка.

Одним з підходів до створення DRY коду, про який я хотів би поговорити, є функціонал в PHP під назвою traits, який дозволяє повторне використання коду в будь-якому класі в кодовій базі.

Коли у нас є клас, ми визначаємо методи, які можуть бути викликані екземпляром класу. Трейти дозволяють нам розширювати наш клас, найчастіше за допомогою додаткових методів, які ми можемо викликати з екземпляра, що імпортував трейт, як у наведених прикладах. Слід зазначити, що методи - це не єдиний спосіб, у який трейти можуть розширювати клас. Вони можуть додавати властивості, статичні методи та поля класу. Або ж трейт може визначати абстрактний метод, який повинен реалізувати базовий клас (хоча в цьому випадку інтерфейс може бути кращим).

По-перше, нам потрібно визначити трейт так само як ми визначаємо клас:

<?php

namespace Foo;

trait Bar
{
public function baz(string $value): string
{
return mb_strtoupper($value)
}
}

Потім у класі ми можемо імпортувати трейт:

<?php

namespace Foo;

class MyClass
{
use Bar;
}

Тепер ми можемо викликати метод baz на екземплярі MyClass, наприклад, echo $myClass->baz('hello');

Але зачекайте, ми можемо піти глибше. Трейти можуть імпортувати інші трейти. 🤯

Замість одного монолітного трейту з усіма нашими додатковими методами, ми можемо згрупувати схожу логіку разом у трейтах, як ви організовуєте логіку у своїх класах. За необхідності можна імпортувати декілька трейтів до класів. Пам'ятайте, що ми можемо використовувати простори імен, щоб допомогти організувати всі наші класи та трейти належним чином.

Що, якщо ми спробуємо імпортувати два трейти у наш клас, і вони обидві визначають один і той самий метод? Якщо ми явно не вирішимо цей конфлікт при імпорті трейтів, то отримаємо фатальну помилку. Для того, щоб вказати, який з двох методів використовувати, ми можемо використовувати оператор insteadof:

<?php

trait One
{
public function hello()
{
echo 'Hello One' . PHP_EOL;
}

public function bye()
{
echo 'Bye One' . PHP_EOL;
}
}

trait Two
{
public function hello()
{
echo 'Hello Two' . PHP_EOL;
}
}

class SomeClass
{
use One, Two {
Two::hello insteadof One;
}
}

$someClass = new SomeClass();
$someClass->hello();
$someClass->bye();

// У результаті буде виведено
// Hello Two
// Bye One