DRY (Don't Repeat Yourself) - кожне знання в системі має мати одне джерело. Дублювання шкодить, бо зміну доводиться вносити в кількох місцях, і рано чи пізно одне з них пропускають.
Але не всяке схоже код - дублювання знання. Два фрагменти можуть виглядати однаково випадково, а змінюватися з різних причин:
// валідація реєстрації
'name' => ['required', 'string', 'max:255'],
// валідація назви товару
'name' => ['required', 'string', 'max:255'],
Це не одне знання: правила для імені користувача й назви товару завтра розійдуться. Спільна функція nameRules() їх штучно зв'яже.
«Неправильна абстракція» (Сенді Метц) - типовий сценарій:
- програміст бачить повтор і виносить код у спільну функцію чи базовий клас;
- з'являється новий випадок, що «майже підходить» - додають параметр;
- ще один випадок - ще параметр, умова всередині;
- через рік абстракція має прапорці
$isAdmin,$skipValidation,$legacyMode, і ніхто не розуміє, як вона працює, а змінювати страшно, бо вона використовується скрізь.
Висновок Метц: «Дублювання значно дешевше за неправильну абстракцію». Якщо абстракція обросла параметрами й умовами, вигідніше повернути код назад (вбудувати в місця використання) і заново подивитися, що справді спільне.
Практичні правила:
- правило трьох: перший раз - пишемо, другий - терпимо дублювання, третій - виносимо абстракцію. До третього разу видно, що справді спільне;
- дублюється знання чи текст? Якщо обидва місця змінюватимуться разом з тієї самої причини - це знання, його варто об'єднати;
- ознаки поганої абстракції: булеві параметри, що вмикають гілки;
if ($type === ...)усередині «спільного» коду; коментарі «для X не викликати»; - дублювання в тестах часто корисне: тест, який читається сам по собі без переходів у допоміжні функції, - кращий тест.
WET («Write Everything Twice») і AHA («Avoid Hasty Abstractions») - жартівливі назви того самого принципу: не поспішати з абстракціями, доки не стане зрозуміло, яка вона має бути.
Докладніше в документації: Сенді Метц: The Wrong Abstraction