2. Obsah
1. Co je to Dependency Injection (DI)
2. Motivace proč se zabývat DI
3. Typy DI
4. Problémy
5. DI Container
6. Závěr - testík
3. Dependency injection (DI)
• Návrhový vzor z rodiny IoC (Inversion of Control).
• Problém : Skryté závislosti třídy (skryté = těžce kontrolovatelné).
• Řešení: zřejmé předávání závislostí = odebrání třídám zodpovědnosti za
získávání objektů, které potřebují ke své činnosti.
• Uplatňuje se princip popisu závislostí pomocíAPI třídy.
• Lze si jej představit pod mottem - “Když něco chceš, tak si o to řekni!”
5. Proč se obtěžovat s DI?
Zvolnění vazeb mezi objekty,
jednodušší testovatelnost,
nutí programátora řešit vazby mezi objekty = čistější přehlednější kód,
pomáhá dodržovat správný objektový návrh,
pravdivé API tříd.
Junior programmerGarry –
“Whatever, I love my spaghetti !”
6. Constructor Injection
• Hlášení závislosti přímo v konstruktoru třídy.
• Problémy:
• Lazy loading (vytváření objektů v momentě, kdy jsou opravdu potřeba),
• Constructor hell (příliš mnoho závislostí v konstruktoru třídy).
public void Company() {
this.worker = new Worker();
this.slacker = new Slacker();
}
// constructor injection
public void Company(Worker worker, Slacker slacker) {
this.worker = worker;
this.slacker = slacker;
}
7. Setter Injection
• Hlášení závislostí přímo při volání setter funkce.
• Lze dobře uplatnit pro inicializaci objektu mimo konstruktor třídy.
@Inject
public void setHelper(Helper helper) {
this.helper = helper;
}
8. Property injection
• Je velmi podobné constructor injection. K hlášení závislostí se používá
anotací přímo u deklarace proměnné.Výhodou je stručnost.
• Nutností je DI container, který bude umět s touto anotací pracovat a
nastavit odkazující proměnnou na již inicializovaný objekt.
public class ClientsController {
// Property injection
@Inject
private final UserDAO userDAO;
public ClientsController() {
}
}
public class ClientsController {
private UserDAO userDAO;
public ClientsController(UserDAO userDao) {
this.userDao = userDao;
// constructor injection
}
}
9. Interface Injection
• Nehlásíme závislost na konkrétní třídu, hlásíme závislost pouze na interface,
• jedná se o velmi silný nástroj – zvyšuje úroveň abstrakce v kódu.
public void setHelper(Employee helper) {
this.helper = helper;
}
public Company(Employee worker, Employee slacker) {
this.worker = worker;
this.slacker = slacker;
}
interface Employee {
public void work();
public void pretendWorking();
}
10. Spaghetti code, je v souladu s DI?
public class Company{
protected Worker worker;
protected Slacker slacker;
public Companz() {
this.worker = new Worker();
this.slacker = new Slacker();
}
public void open(){
this.worker.work();
this.slacker.pretendWorking();
customer = new Customer();
System.out.println(“<h1> Welcome! How can I help you?</h1>”);
this.worker.serverCustomer();
System.out.println(“<p>” + customer.wish + ”</p>”);
}
}
Senior programmer John - “Garry, you are fired!”
11. Problémy - Constructor hell
public function Controller(IHttpRequest request, IHttpResponse response,
User user, UserModel userModel, Language language,
DataSource dataSource, ....) {
// rest of the code
}
• Přílišné množství parametrů již přímo v konstruktoru třídy,
• odhaluje tak nesprávné rozvržení tříd,
• řešit lze za pomocí refaktoringu tříd.
Jesus Christ, so many dependencies!!
12. Problémy - DI vs. Lazy loading
• Lazy loading – návrhový vzor, kdy se vytváření instance objektu se odkladá
do doby než je opravdu potřeba,
• z důvodu optimilizace je přínosné tento návrhový vzor používat,
• naopak DI požaduje pro své třídy již vytvořené instance (závislosti).
Junior Garry – No way, they troll me all the time!
13. Service Locator – zlé dvojče DI
• Jedná se také o návrhový vzor, nicméně jde proti smyslu DI (antipattern)
• Třída se totiž nehlásí k jednotlivým závislostem.Všechny závislosti získává
skrze globální objekt (service locator).
• Nicméně lze přejít pak jednoduše k DI.
public class ClientsController {
private final UserDAO userDAO;
public ClientsController(ServiceContainer $services) {
this.userDao = (UserDao)$services->getService("UserDAO");
}
}
Hell yeah, your classes are liars again!
14. DI Container (DIC)
• Příliš mnoho závislostí (závislé třídy) => potřebujeme je spravovat, k těmto
účelům slouží právě DI kontejner,
• DI kontejner dává již připravené instance tříd vždy jeho žadateli,
• pravidlem je, že o existencí DI kontejneru by mělo vědet minimální množství
kódu,
• příklad: Google Guice Cointainer (není jediný).
15. Pohled na aplikaci jako stavbu tříd
Application
Database
Connection DaoServices
Resources
DataSource
GUI
Other
Clasess…
16. Na co teda DI container?
• Pokud svou aplikaci popíšete jako strom závislostí. Není nic jednoduššího
než si ji nechat sestavit právě DI Containerem.
• Ten drží konfiguraci tříd celé aplikace, nahrazuje pokoutné Environment
objekty.
Hey container, gimme the Application!
Yess, sir!
17. Konfigurace DI containeru
• Slouží k správné inicializaci prostředí aplikace, resp. jejich tříd.To, jakým
způsobem se konfigurace provádí, záleží na konkrétní implementaci
containeru (anotace, třídní objekt, xml soubor...)
import com.google.inject.Binder;
import com.google.inject.Module;
import com.google.inject.Scopes;
public class DependencyConfig implements Module {
@Override
public void configure(Binder binder) {
binder.bind(Storage.class).to(DatabaseStorage.class).in(Scopes.SINGLETON);
}
//example of Google Guice Container configuration
}
18. DIC a persistence objektů
• DI kontejnery nám umožňují řídit životnost objektů => obrovský potenciál DI
• Problém návrhového vzoru Singleton:
• Nikdy nemůžeme dopředu vědět zda třída bude opravdu Singletonem či nikoli.
• Není přímou zodpovědností třídy se starat zda bude jedináčkem či nikoli.
• Za pomocí DI kontejneru se tyto problémy dají vyřešit.
19. DIC – není Singleton jako Singleton
@Singleton
public class Company {
protected Worker worker;
protected Slacker slacker;
public Firma() {
this.worker = new Worker();
this.slacker = new Slacker();
}
}
public class Company {
private static Company instance = null;
private Company() {}
public static Company getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
20. Kontrolní otázky
1. V čem je podstata návrhového vzoru Dependency injection (DI)?
2. Jaké výhody přináší DI?
3. Jaké máme typy DI?
4. Jaké problémy mohou nastat při aplikování vzoru DI?
5. K čemu slouží Dependency Injection Container?
6. Dalo vám to něco?