dependency injection
DESCRIPTION
Dependency Injection. Ať se postará někdo jiný, najmeme si programátory z Číny. Čuníkům vstup zakázán. Co budeme dneska dělat?. Zopakujeme si základní principy DI. Dozvíme se, jak může DI usnadnit programování. Ukážeme si, jak psát přehlednější a čitelnější kód. - PowerPoint PPT PresentationTRANSCRIPT
Dependency InjectionAť se postará někdo jiný,
najmeme si programátory z Číny.
Čuníkům vstup zakázán
Co budeme dneska dělat?Zopakujeme si základní principy DI.Dozvíme se, jak může DI usnadnit programování.Ukážeme si, jak psát přehlednější a čitelnější kód.Předvedeme si, jak se zbavit skrytých závislostí v
kódu a statického volání.Vše si demonstrujeme na příkladu z praxe, který si
společně zkritizujeme a opravíme.
V čem je to dobré?Minimálně se vyznáte ve vlastním kódu, i když se na
něj podíváte třeba za půl roku.
Dependency InjectionJak poznám, že je třída závislá na jiné třídě?Co je to skrytá závislost?V čem nám DI pomáhá?Jaké znáte typy DI?
Dependency Injection ≠ čistý kód
DI + ≠ jen k němu vede
Dependency InjectionVychází z návrhového vzoru Inversion of
Control.Odebírá třídám odpovědnost za vytváření
objektů, na kterých jsou závislé.Řízení je delegováno na nadřazený objekt.
Klasický přístuppublic class Computer() {
private OsuNetwork network;
public Computer() {this.network = new OsuNetwork();
}}
Připojení k síti je služba, ne součást počítače.
Vzniká připojení k síti uvnitř počítače?
Použití DIpublic class Computer() {
private OsuNetwork network;
// Constructor Injectionpublic Computer(OsuNetwork network) {this.network = network;}
// Setter Injectionpublic void setOsuNetwork(OsuNetwork network) {this.network = network;}
}
Kde seženu připojení k síti?„Ať se postará někdo jiný.“
Výrobce počítače se nemusí starat o připojení k síti, to řeší uživatel počítače (třídy počítač)
Použití DI a rozhranípublic class Computer() {
private Network network;
// Constructor Injectionpublic Computer(Network network) {
this.network = network;}
// Setter Injectionpublic void setNetwork(Network network) {
this.network = network;}
}
Dependency InjectionZávislost na jiných objektech jasně
deklarujeme v konstruktoru třídy nebo v jejich metodách.
Použití operátoru new uvnitř třídy je skrytá závislost.
Použití statického volání uvnitř třídy je skrytá závislost.
Výjimku tvoří primitivní typy a nativní třídy jazyka.new Class() Class.getInstance()
Homer a závislosti
Příklad na cvičeníPříklad ke cvičení je ke stažení na následující
adresehttp://tinyurl.com/mo4omp3
Příklad na cvičenípublic class Application {
public Application() {Article article = new Article();article.setHeadline("Nadpis článku");article.setText("Text článku");article.save();
}}
Kam se článek uloží? Do souboru nebo do databáze?Jakou databázi používám?Jaké jsou parametry připojení?
Já myslel/a, že to víš …Když něco potřebuju, tak si o to řeknu!Třída Article nemá žádné viditelné závislosti, ale
opravdu je nemá?Co se stane, když smažeme všechny ostatní třídy?
article->save(); // ERROR: Class MysqlStorage not found// ERROR: Class DatabaseConfig not found
Kdo by to čekal?
Statické peklo v akcipublic class Article { private String headline; private String text; public void save() { MysqlStorage storage = MysqlStorage.getConnection(); storage.executeQuery("INSERT INTO articles…“); }}
Jaký návrhový vzor jsme použili? Nápověda: Class.getInstance();
Jedná se o porušení DI? Jaké je řešení?
Řešení?public class Article { private String headline; private String text; public void save(MysqlStorage storage) {
storage.executeQuery("INSERT INTO articles…“);
}}Bude to fungovat? Co ještě musíme upravit?
Zase o krok dále…Třída MysqlStorage je singleton. Převedeme ji na
klasickou třídu s veřejným konstruktorem.
public MysqlStorage() { DatabaseConfig config = new DatabaseConfig(); this.server = config.getServer();
// some code }
Je to v pořádku?Jsou všechny závislosti nahlášeny?
Je to v pořádku? public MysqlStorage(DatabaseConfig config) {
this.server = config.getServer();// some code
}
K zamyšlení …public class Article { private String headline; private String text; public void save(MysqlStorage storage) {
storage.executeQuery("INSERT INTO articles…“); }}
Je správné, aby třída Article věděla o struktuře DB?Co když budeme chtít uložit článek do souboru?
Upravte kódpublic class Article { private String headline; private String text; public void save(Storage storage) {
storage.save(this); }}
Nesmíte zasáhnout do třídy MysqlStorage ani FileStorage
Jak zajistit společné rozhraní? public class FileStorage { public void save(Article article) { System.out.println("Article was saved to file"); }}
public class MysqlStorage { public void executeQuery(String query) { System.out.println("Record was saved to database
(" + query + ")"); } }
Použijeme adapterNávrhový vzor adaptér použijeme, pokud
potřebujeme, aby třída měla jiné rozhraní než to, které právě má.
Adaptér slouží jako prostředník mezi prostředím, které požaduje nějaké rozhraní, a třídou, jejíž rozhraní neodpovídá požadovanému. Umožňuje tedy spolupráci třídám, které by spolu jinak nespolupracovaly.
Je to lepší? public Application() { Article article = new Article(); article.setHeadline("Nadpis článku"); article.setText("Text článku"); DatabaseConfig config = new DatabaseConfig(); MysqlStorage mysqlStorage = new
MysqlStorage(config); DatabaseStorage databaseStorage = new
DatabaseStorage(mysqlStorage); article.save(databaseStorage); }
Sestavení závislostí pomocí containeru
Application
DI container
DI container@Inject – ohlášení DI containeru, že má
obsloužit třídu.@Singleton – označení třídy jako singleton@ImplementedBy(ServiceImplementation.cla
ss)
S použitím DI containeru @Injectpublic Application(Article article, Storage
storage) { article.setHeadline("Nadpis článku"); article.setText("Text článku"); article.save(storage); }
ÚkolVytvořte jinou třídu, která má závislost na
rozhraní Storage.Přihlašte se k závislosti a sledujte v konzoli,
kolikrát se vytvoří připojení k DB.Jak použít singleton spolu s DI?
A jak to dopadlo se singletonem?@Inject@Singletonpublic DatabaseStorage(MysqlStorage
storage) { this.storage = storage;}
Hurá, singleton se vrátil!Win: databázové připojení se vytvoří jen jednou.Win: žádné skryté závislosti.
Rekapitulace: proč zvolit DI?Jasné vazby mezi objekty,pravdivý a předvídatelnější kód,přehlednější, lépe upravitelný,žádné statické volání,znovu použitelnost kódu,mnohem lepší testovatelnost.
Zvládli jsme to!