280 likes | 397 Views
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.
E N D
DependencyInjection 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. • 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.
DependencyInjection • Jak 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? DependencyInjection ≠ čistý kód DI + ≠ jen k němu vede
DependencyInjection • Vychází z návrhového vzoru InversionofControl. • 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řístup Vzniká připojení k síti uvnitř počítače? publicclassComputer(){ privateOsuNetwork network; publicComputer(){ this.network=newOsuNetwork(); } } Připojení k síti je služba, ne součást počítače.
Použití DI Kde seženu připojení k síti? „Ať se postará někdo jiný.“ publicclassComputer(){ privateOsuNetwork network; // ConstructorInjection publicComputer(OsuNetwork network){ this.network= network; } // SetterInjection publicvoidsetOsuNetwork(OsuNetwork network){ this.network= network; } } 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í publicclassComputer(){ private Network network; // ConstructorInjection publicComputer(Network network){ this.network= network; } // SetterInjection publicvoidsetNetwork(Network network){ this.network= network; } }
DependencyInjection • Zá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. newClass() Class.getInstance()
Příklad na cvičení • Příklad ke cvičení je ke stažení na následující adrese • http://tinyurl.com/mo4omp3
Příklad na cvičení publicclassApplication{ publicApplication(){ Articlearticle=newArticle(); 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 Articlenemá žádné viditelné závislosti, ale opravdu je nemá? • Co se stane, když smažeme všechny ostatní třídy? article->save(); // ERROR: ClassMysqlStorage not found // ERROR: ClassDatabaseConfig not found Kdo by to čekal?
Statické peklo v akci publicclassArticle{ privateStringheadline; privateString text; publicvoidsave(){ MysqlStoragestorage=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í? publicclassArticle{ privateStringheadline; privateString text; publicvoidsave(MysqlStoragestorage){ 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. publicMysqlStorage(){ DatabaseConfigconfig=newDatabaseConfig(); this.server=config.getServer(); // somecode } Je to v pořádku? Jsou všechny závislosti nahlášeny?
Je to v pořádku? publicMysqlStorage(DatabaseConfigconfig){ this.server=config.getServer(); // somecode }
K zamyšlení … publicclassArticle{ privateStringheadline; privateString text; publicvoidsave(MysqlStoragestorage){ 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ód publicclassArticle{ privateStringheadline; privateString text; publicvoidsave(Storagestorage){ storage.save(this); } } Nesmíte zasáhnout do třídy MysqlStorage ani FileStorage
Jak zajistit společné rozhraní? publicclassFileStorage{ publicvoidsave(Articlearticle){ System.out.println("Article was saved to file"); } } publicclassMysqlStorage{ publicvoidexecuteQuery(Stringquery){ System.out.println("Record was saved to database ("+ query +")"); } }
Použijeme adapter • Ná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ší? publicApplication(){ Articlearticle=newArticle(); article.setHeadline("Nadpis článku"); article.setText("Text článku"); DatabaseConfigconfig=newDatabaseConfig(); MysqlStoragemysqlStorage=newMysqlStorage(config); DatabaseStoragedatabaseStorage=newDatabaseStorage(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.class)
S použitím DI containeru @Inject public Application(Article article, Storage storage){ article.setHeadline("Nadpis článku"); article.setText("Text článku"); article.save(storage); }
Úkol • Vytvoř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 @Singleton publicDatabaseStorage(MysqlStoragestorage){ 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.