Як послуги впливали на поведінку введення залежностей розробника - Flagbit Blog

впливали

Принаймні з тих пір, як постійно зростає популярність модульних тестів, особливо за допомогою PHPUnit, вирішення залежностей класів за допомогою ін'єкції залежностей у PHP відіграє все більш важливу роль у повсякденному житті розробників. Класи, які мають одну або кілька залежностей від інших типів класів, передаються, коли вони створюються за допомогою конструктора або під час виконання за допомогою викликів методів. Це покращує ремонтопридатність окремих класів, і залежності можна обмінювати легше, якщо вимоги змінюються. Також покращується тестованість окремих компонентів, в яких залежності класу можуть бути замінені простими тестовими подвійними. Однак недоліком є ​​збільшена кількість шаблонного коду, коли однакові об’єкти завжди повинні бути створені в різних точках проекту, щоб покрити однакові залежності.

Так званий службовий локатор був введений в діючі фреймворки PHP. Завданням локатора служб є внутрішнє вирішення та створення екземплярів залежностей від служби. Це означає, що екземпляри об'єктів можна створювати за допомогою служби без необхідності додавати шаблонний код для вирішення залежностей у бізнес-логіці. Крім того, службовий локатор забезпечує єдиний екземпляр викликаної служби в запиті, щоб уникнути непотрібного створення об'єктів без недоліків класичного синглтона.

Однак з часом простота використання Служби пошуку локаторів спрямовувала використання введення залежностей у напрямки, які можна швидко реалізувати технічно, але які не зовсім відповідають його меті. Розчинення залежностей перетворилося на барвисте злиття класів.

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

Ін’єкція службового локатора

Мабуть, найвідоміша шкідлива звичка при використанні Локатор послуг полягає в додаванні його як залежності до свого класу. Зазвичай супроводжується аргументом "Можливо, мені все-таки потрібна одна з послуг", ця закономірність є більше ознакою того, що людина недостатньо запланувала свій розвиток. Жоден клас ніколи не потребує всіх послуг. Іноді ви також можете знайти коментарі в Google, які вважають, що це нормально, якщо фабриці надається Службовий локатор. Навіть фабриці для функціонування потрібно мало послуг. Якщо існує залежність більше ніж від 4 інших служб, вам слід ще раз подумати про завдання та структуру заводу.

Складність особливо очевидна при написанні модульних тестів. Клас зазвичай можна використовувати для ідентифікації залежностей від голови методу конструктора або методів сеттера. Коли додається службовий локатор, тестовий дубль повинен бути створений не тільки для нього, але і для всіх фактичних залежностей, які отримуються з нього. З коду також важко визначити, який насправді тип послуги. Оскільки клас у PHP також може містити методи, які не походять від інтерфейсу, взаємозамінність класів з тим самим інтерфейсом більше не гарантується.

В ідеалі залежності слід передавати, а не брати з іншого об'єкта.

Наприклад, якщо ви подивитесь на Symfony 2, то побачите, що тут також використовується безпосередньо Сервісний локатор. Найвідоміші приклади - це контролери та команди, але лише за умови, що вони реалізують інтерфейс ContainerAware, що є стандартним випадком. Але є також варіанти для контролерів та команд, щоб цього уникнути.

Лісозаготівля

Запис інформації в журнал може бути більш важливим для одного проекту, ніж для іншого, і може відрізнятися за обсягом інформації. Ось чому часто існує підхід до постійної вставки екземпляра реєстратора як залежності в службі класу. Однак у дуже небагатьох випадках реєстратор насправді є залежністю, тому що спочатку потрібно задати собі питання: "Чи буде моя функція все ще працювати без реєстратора?"

Залежно від дизайнерського рішення, може бути достатньо виключити всередині класу та додати запис журналу в блок catch на додаток до обробки помилок.

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

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

Забагато залежностей

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

Багато контейнерів для введення залежностей пропонують можливість встановлення залежностей служби, використовуючи методи налаштування в конфігурації. Як результат, деякі розробники схильні встановлювати залежності за допомогою методів сетера замість того, щоб використовувати конструктор, але це не вирішує проблему. Навпаки: сетери слід встановлювати явно і лише тоді, коли об’єкт уже існує. Конфігурація служби приймає використання сетерів, але цей клас можна використовувати лише з відома цих сетерів, наприклад, для модульних тестів, і якщо не встановлені обов'язкові залежності, розробник також повинен подбати про обробку помилок . Це надмірно збільшує уражений клас. Нічого з цього не потрібно при введенні залежностей через конструктор. То як слід вирішувати цю проблему?

Це хороший знак того, що клас робить більше, ніж мав би бути.

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

Дозвіл залежностей

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

У наступному прикладі клас Foo має залежність від $ myService, яка передається в конструкторі.

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

Недолік: Класи, які використовують Foo та його метод doSomething, мають залежність від $ myService.

Як другий підхід, слід також розглянути, чи завдання методу doSomething не належить до його власного класу і чи має бути налаштовано як окрема служба. Однак ця нова послуга не обов'язково повинна бути залежністю класу Foo.

У цьому прикладі коду завдання методу doSomething було повністю видалено з класу Foo і реалізовано в новому класі “Qux”, який як служба має залежність від $ myService у попередньому прикладі.

Звичайно, цей приклад є простим, оскільки у випадку рефакторингу існують інші перешкоди. Коли структура класу змінена, зміни повинні бути зроблені у всіх місцях, де використовувався Foo. Змінити структуру класу ще складніше, якщо це визначено інтерфейсом.

Ін’єкція залежностей та послуги

Введення залежності - загальний термін, який має безліч різних форм реалізації. Рішення щодо того, яку з форм слід використовувати в конкретному випадку, не може приймати контейнер для введення залежностей або локатор служб, але повинен вирішувати сам розробник. Фреймворки пропонують нам функціональні можливості, які допомагають нам у розвитку та повинні зменшити багато нудних кроків. Як його використовувати, все ще залежить від нас.