Dependency Injection (DI)
Когато можем да инджектваме даден обект в друг обект.
Така се реализира design принципът Inversion of Control, демек – на практика реализиране на Single Responsibility SOLID принципът.
Types of Dependency Injection
- Constructor Injection – през конструктора, демек инджектваният обект се подава oще на конструктора на основният клас и се сетва като пропръти още при инстанциране на основният обект.
- Property (Setter) Injection – идеята е подобна на горната, пак инджектваният обект да бъде сетнат на клас пропърти, но не през конструктора, а през сетър.
- Method Injection – тук имаме ситуация, подобна на Property (Setter) Injection но с разликата, че основният клас имплементира интерфейс, който го задължава (него, основният) да има метод, който да играе ролята на сетър, и по този начин задължава основният клас да приеме даден обект.
Например клас Car имплементира интерфейс EngineMountable, в който има метод engine() и по този начин класът Car e длъжен да има инджектнат обект за двигател.
Добър въпрос би бил, ако Car наследява например абсклас, не може ли в него да е изнесен абстрактен сетер/сетери, които да играят същата роля, тоест вместо интерфейс да имаме абсклас.
Не съм съвсем наясно с идеята на този тип injection, признавам си. Може би идеята е да се зададе някакъв централизиран начин за инджектване на обекти, ако имаме абсклас, или вероятно ако е интерфейс, да задължи имплементиращите обекти да имат такива „entry points“ за инджектване на обекти…
Inversion of Control (IoC)
Inversion of Control (IoC) е desing principle, която използва DI, при който идеята е части от цялостната функционалност на даден клас, да се „разхвърля“ или разпредели по други класове, с цел да се спази SOLID Single Responsibility Principle и основният клас да бъде олекотен, и след това когато ни трябват тези „разхвърляни“ функционалности, обекти на тези класове да бъдат инджектвани в основният клас.
Taка се постига и high cohesion вътре в класа както и low coupling между отделните класове.
Dependency Inversion Principle (DIP)
Dependency Inversion Principle (DIP) е SOLID принцип, нещо като продължение или разширение на IoC, в смисъл такъв, че повелява основният клас, в който ще инджектваме обектите на класовете, които сме „разхвърляли“, да може да приема като входни параметри не обекти от конкретен клас, а от различни класове, които обаче екстендват или имплементират даден абсклас или интерфейс.
Демек, „дай ми нещо, с което да отвинтя тази гайка, а не конкретен гаечен ключ“…
Идеята и на двата принципа е да се постигне максимално т.н. „loose coupling“ и максимална кохезия вътре в класа. Припомнете си статията „Cohesion and Coupling“.
Но работата е е там, че само с прилагане на IoC не става, демек само IoC не стига за да се постигне това.
Тоест, основният клас сякаш казва предварително: „не искам конкретна функционалност и обект от конкретен клас, давайте ми обекти, които по принцип ще ми свършат работата“, демек имплементиращи даден интерфейс.
Taка постигаме „high-level modules should not depend on low-level modules“.
Защо?
Защото в случая, high-level модулът при нас е основният клас, а low-level са тези, които му инджектваме, които вършат някаква по-конкретна и специфична работа, тоест са по-ниско в нивото на абстракция.
И основният става по-малко зависим от инджектваните, защото може да приема по-широк диапазон от обекти, а не конкретно само от един клас.
Пример: ти си основният клас и искаш да си викнеш (инджектнеш) майстор да ти смени водомера.
Кога си по-малко зависим (бидейки high-level module) от майстора (който е low-level module)? Ако трябва да можеш да викаш само един конкретен майстор или когато можеш да извикаш всеки майстор, стига да имплементира VodoprovodchikInterface?
Общото между тях?
Общото между IoC и DIP е целта – да се намали зависимостта между отделните класове. Например, да не се създава обект от един клас вътре в друг клас, защото така твърдо свързваме двата класа и единият вече не може без другият, конкретно „другият“, то е ясно, че все някакъв обкет трябва да му инджектнеш, освен ако не си му задал default стойност null.
Демек, когато инджектваме обект, да не очакваме параметърът през който го инджектваме да е от конкретен клас, а да е по-общо – от конкретен интерфейс. Така казваме, че инджектваме не конкретна функционалност, а който по принцип прави това, което ни трябва. Без задължително да е обект от този конкретен клас.
Your code gets decoupled so you can easily exchange implementations of an interface with alternative implementations.
Разликата между тях?
Разликата е, че DIP e по-скоро продължение, надстройка на IoC. Демек, IoC повелчва функционалността на един клас да се „разхвърля“ на под-класове, а DIP повелява това разхвърляне да стане така, че „разхвърляният“ клас да може да приема не конкретни функционалноти, а функционалности по принцип.
Литература:
https://www.tutorialsteacher.com/ioc/introduction
https://www.tutorialsteacher.com/ioc/dependency-inversion-principle