Dependency Injection – добър дизайн или излишно усложняване

В университета имахме да пишем есе за DI – добро или лошо. Харесали са това което сглобих за час и половина след катеренето … та реших да го споделя.

Dependency Injection и Inversion of

Control – добър дизайн или излишно

усложняване

Ангел Коилов

ф.н. 0801261017

Един от известните шаблони за дизайн е Dependency injection. Идеята му е, че когато създаваме функионалност, не трябва да я правим така, че да работи за точно определен случай с точно определени данни.

Да кажем че имаме програмист на име Иван. Иван знае за какво е и кога е добре да се използва Dependency injection. Той е написал функционалност за копиране. Използва абстракни класове за реализирането. Кодът му изглежда по следния начин Copy(Reader& r, Writer& w) . Всичко е супер и Ваньо има възможност да ползва Copy() с „четене” от бар код, клавиатура и скенер; може също да „пише” върху принтер, да изкарва резултата на монитора и да го щампова върху тениски. В съседното село живее върло компютърно гуру, известно в онлайн общоносттва като Авксений Хакера. Авксении има „уникалната” дарба да пише вируси за skype, да сваля данни от компютрите на приятелите с предварително инсталиран ftp сървър, да прейнсталира „бозата” и да решава задачите за девети клас по информатика. Гуруто вижда какво е направил Иван и веднага му удва на ум, че проекта който „пише” в момента се нуждае от точно такава функционалност. Авксении разработва онлайн магазин, подобен на ebay, но ще е с повече продукти и по-евтини, ще има търсачка подобна на google, но по-добра; ще е нещо супер-хипер мега яко. Ненадминатия ни компютърджия безпогрешно разпознава къде е необходимо да се използва черната магия наречена Dependency injection,а именно в стандартния изход. За целта се прави функция от вида echoCool(echoAble $object, outputAble $output). Така всичко което ще се подава на браузера, ще трябва да минава през тази функция. Цялата идея е, че ще трябва всеки ден в 00:00 часа, да пускаме един скрипт да праща „информативни” смс-и до „абониралите” се „потребители” за новите стоки и промоции. Обеден в себе си, Авксений смело пренаписва 6000 реда вложени if-ове и switch-ве. Най-накрая всичко е говото и навсякъде се ползва echoCool(). Всички string-ове са заменени с класове, който имат метод print(). За да защитим систамата си от пишман-хакери по подразбиране print() има параметър и всъщност излежда по следния начин: print($useHtmlSpecialChars = true), за да не се налага на владетеля на тъмната страна на бинарния код да пише на толкова много места досадното escape-ване за browser-ите. Написваме и модулът за sms-те. Той за втори параметър на echoCool подава такъв параметър, че резултатът се съхранява във файл, за да не товарим базата, за всеки потребител се изчита файла и се праща съобщение. Всичко е супер яко. Авксений си щрака с пръсти по цял ден и чат пат дописва някой if. Един леко мъглив следобед в творението на Авксении се появява стока със странното име „Nice cool <<pro>>“. Проблемът е че по подразбиране $яке->print() връща като резултат „Nice cool &lt;&lt;pro&gt;&gt;”, така трябва да се пусне към browser-а; но не и към sms центъра, защото „>“ не е специален знак. Творението, връх на инженерната мисъл не работи както трябва. Авторът му получава все повече грешни sms-и. Време е за fix. Владетелят на тъмната страна веднага измисля супер ефективен и бърз фикс.

 

echoCool(echoAble $object, outputAble $output) {

$text = $object->print();

if (get_class($output) == ‘sms’) {

$text = htmlspecialchars_decode($text);

}

$output->out($text);

}

Стана така, че нашият шаблон е „immobile” защото зависи от конкретната имплементация на print(). Въпреки че ще напишем fix-овете на едно място, това си остава лош код.

Не само че си останахме с предишните проблеми, ами и при всяко изкаране на резултат, се нуждаем от клас, interface и функцията която прави самото извикване.

  • Класът – на места нямаме нужда от клас, даже нямаме нужда от функция, а просто извеждане на string. В този случай, не само че забавяме значително операцията, поради нуждата от създаване на обект, но и трябва да напишем това създаване някъде – ненужен код
  • Зареждането на класа от autoloader, всеки нов клас колкото и малко да е – забавя.
  • Функцията – бави с проверките, бави с това че извиква методи.
  • Interface – това, по принцип означава още един inteface за доста класове, още един файл, още бавене при създаването на класа.

Всичко това от горе, ще повлияе на

  • кеширането – първоначалното зареждане – четем няколко файла повече
  • opcode кеширането – кешираме повече информация.
  • Значилно забавяне на дебъгването на проекта – за да проследим какво прави print() трябва да влезем във функцията echoCool()
  • Ненужния код го пазим в системите за контрол на версиите. Всички си пазим тези backup-и на много места и по много
  • „Пиши кратко и разбирамо”, за жалост средностатистическия програмист има нужда от доста време да разбере шаблоните за дизайн. А щом използваме Dependency injection със сигурност ще ползваме и други, така на начинаещите ще им отнеме много време да навлязат даже и в най-простите неща.

Всеки шаблон е направен да решава определен проблем, това важи и за Dependency injection, няма магически начин по който да решаваме всички проблеми. Трябва да знаем какво, как, защо и къде да го ползваме. Често се ползва Singleton за пазене на инстанцията за връзка с базата данни, но когато се появи нужда от втора връзка към друга база едно ужасно решение е „Нека добавим параметър към getInstance()”, е да ама това всещност вече не е singleton.

Dependency injection е добър в следните ситуации:

  • Трябва да подадем информация за конфигурацията на един или повече компоненти.
  • Трябва да добавим една и съща зависимост на няколко компонента.
  • Трябва да добавим различни реализации на една и съща зависимост.
  • Да добавим една и съща реализация за различни конфигурации
  • Имаме нужда от фунционалност, предоставена от подавания параметър.

Dependency injection не е добър в следните ситуации:

  • Никога, по никакъв начин, в никакъв случай няма да реализираме различна имплементация
  • Никога, по никакъв начин, в никакъв случай няма да се нуждаем от различна конфигурация

Ако винаги използваме Dependency injection с идеята, че ще можем лесно да променяме нещата определено сме в грешка. Архитектурата на software-а ни със сигурност има места на които това е възможно и без конкрения шаблон, а както спонемах неговото ползване има значително отрицателно влияне върху производителността. Dependency injection улеснява unit testing-а,а това е голям плюс.

Като заключение ще повторя по-горе казаното: Dependency injection е шаблон за дизайн. Всеки шаблон е добър за това, за което е измислен. Няма шаблон който трябва да се използва винаги и навсякъде, ако някой се е натъкнал на такъв – моля да сподели.

източници:

http://www.objectmentor.com/resources/articles/dip.pdf

http://www.potstuck.com/2009/01/08/php-dependency-injection/

http://www.tonymarston.net/php-mysql/dependency-injection-is-evil.html

This entry was posted in Miscellaneous, Programming. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *