Forum » Programiranje » Dependency Injection
Dependency Injection
abyssus ::
Trenutno se učim ASP.NET MVC. Delam po eni knjigi, v kateri je eno poglavje namenjeno dependency injection. Na ta pojem sem naletel že pred časom, vendar se z njim nisem ubadal prav tako pa nikjer nisem zasledil potrebe po uporabi le-tega. Vendar me je sedaj vseeno mučilo, kaj je to in sem poglavje prebral. Problem je v tem, da kljub temu še vedno nimam pojma, kaj to dejansko je, kakšne težave lahko pričakujem, če tega ne uporabim in tako naprej. Zato me zanima, če je kdo tukaj pripravljen mi po "kmečko" napisati, kaj to dejansko je in vse tako naprej.
Res hvala za vse odgovore!
Res hvala za vse odgovore!
Matek ::
Reddit ima za te reči Explain like I'm five: Klik.
Bolje ispasti glup nego iz aviona.
Zgodovina sprememb…
- spremenil: Matek ()
mihies ::
Zelo na kratko. DI/IOC (inversion of control) ti omogočata (lažje) testiranje (unit testing) in lažje menjavanje kosov aplikacije.
http://blog.rthand.com/
SLODUG - uporabniška skupina
https://www.facebook.com/groups/slodug/
SLODUG - uporabniška skupina
https://www.facebook.com/groups/slodug/
Spura ::
mihies ::
Nisem napisal kaj to je, ker lahko pogledaš npr. na
Dependency injection @ Wikipedia
Bistvene prednosti sem pa napisal, al ne?
Dependency injection @ Wikipedia
Bistvene prednosti sem pa napisal, al ne?
http://blog.rthand.com/
SLODUG - uporabniška skupina
https://www.facebook.com/groups/slodug/
SLODUG - uporabniška skupina
https://www.facebook.com/groups/slodug/
Zgodovina sprememb…
- spremenil: mihies ()
Iluvatar ::
Dependency injection je zelo težko razložit v par stavkih. Ko podaš definicijo, si je težko predstavljat kaj je zadaj. Osebno se najboljše učim iz primerov, zato bom podal definicijo in primerček. (v C#, kjer sem najbolj domač) Jaz bi Dependency Injection definiral takole: Dependency Injection je množica "software design" tehnik in vzorcev (patterns), ki ti omogoča razvoj razsklopljene kode (loosely coupled code). Če si zamisliš problem, da je tvoja naloga izdelati nek razred oz. neko kodo ki vrne neke podatke o osebi iz baze, je zadeva na prvi pogled precej trivialna. Naredil boš razred npr. IPersonRepository, v njem boš imel metodo VrniPodatke, implementacija te metode pa bo vzpostavila povezavo z neko podatkovno zbirko in vrnila določene podatke. OK. Job done. Sedaj pa ti npr. šef reče, da so ti podatki o osebi lahko v CSV datoteki ali pa so na voljo preko nekega spletnega servisa, čez nekaj časa, bodo na voljo tudi v XML datotekah. Implementacijo teh scenarijev lahko zelo dobro podpreš z uporabo Dependency Injection. Ideja je da za posamezne scenarije (XML, baza, service) uporabiš različne repozitorije. Recimo Service repozitorij bo dobival podatke o osebi iz servisa, CSV repozitorij bo dobival podatke iz CSV datoteke, SQL repozitorij bo dobival podatke iz SQL-a
Naredimo najprej interface IPersonRepository in definirajmo metodo VrniPodatke. Da bo vse bolj enostavno, bo vrni podatke vračala string, v svoji real world aplikaciji boš verjetno želel vrnit nek Oseba razred jaz sem tukaj namenoma uporabil string da je bolj enostavno (gre za Demoware)
Torej interface.
Sedaj naredimo še posamezne repozitorije, ki vsi implementirajo ta interface, implementacijo metod bom namenoma izpustil vračal bom samo različne stringe glede na repozitorij
Ok. sedaj imamo implementirane repozitorije. Imejmo razred RepositoryManager s konstruktorjem v katerega podamo želeni repozitorij, smo s tem uporabili Constructor Injection (eden od principov in najbolj pogost v Dependency Injection). Ker si pri implementaciji uporabil Interface bo konstruktor razreda RepositoryManager sprejel parameter tipa IPersonRepository, torej razred RepositoryManager ne bo imel odvisnosti (dependency) na razrede CSVRepository , ServiceRepository in SQLRepository ampak samo na interface IPersonRepository. V razredu RepositoryManager imejmo metodo PridobiPodatke, ki go klicala metode iz repozitorija, glede na to kateri repozitorij je bil določen v konstruktorju. Tako si razsklopil svojo kodo. Če bo potrebno pridobivati podatke iz baze, boš v ta parameter podal instanco razreda SQLRepository in pridobival boš podatke iz baze, če boš podal instanco razreda CSVRepository, boš pridobival podatke iz CSV datoteke ipd...
Primer implementacije tega razrade bi bil:
Recimo konzolna aplikacija, ki uporabi vse našteto bi izgledala takole:
Kot vidiš si na lep in "clean" način skodiral aplikacijo. V konkretnem primeru zgoraj, si uporbil CSVRepository in boš pridobil podatke iz CSV datoteke. Če si sedaj šef izmisli da moraš pridobiti podatke še iz XML datoteke, boš izdelal razred XMLRepository, ki implementira interface IPersonRepository in boš v svoji aplikaciji spremenil samo tole vrstico:
Še bolj si lahko olajšaš življenje, če uporabiš t.i. DI Container-je. To so implementacije posameznih tehnik DI in ti omogočajo, da to kar si ti naredil z vrstico
To pomeni, da bi Unity v Run-Time poskrbel da se bodo vsi IPersonRepository resolvali v CSVRepository. Konkretno ti to omogoča, da lahko sedaj svojo aplikacijo namestiš nekomu, ki ima podatke v SQL strežniku in ti ne bo potrebno ponovno prevesti aplikacije in uporabiti ustrezen repozzitorij, spremenil boš samo konfiguracijo v XML v
Ufff, dolg post sem napisal, pa saj sem rekel, da ne gre to na kratko razložit :) Upam, da ti je sedaj kaj bolj jasno, drugače pa kaj vprašaj :)
Edit: typos
Naredimo najprej interface IPersonRepository in definirajmo metodo VrniPodatke. Da bo vse bolj enostavno, bo vrni podatke vračala string, v svoji real world aplikaciji boš verjetno želel vrnit nek Oseba razred jaz sem tukaj namenoma uporabil string da je bolj enostavno (gre za Demoware)
Torej interface.
public interface IPersonRepository { string VrniPodatke(); }
Sedaj naredimo še posamezne repozitorije, ki vsi implementirajo ta interface, implementacijo metod bom namenoma izpustil vračal bom samo različne stringe glede na repozitorij
public class SQLRepository : IPersonRepository { public string VrniPodatke() { //tukaj bi bila koda, ki pridobiva podatke iz baze, tvoriš connection, poizvedbo, ali uporabiš npr. entity framework return "Podatki o osebi iz SQL Repoitory" } } public class ServiceRepository : IPersonRepository { public string VrniPodatke() { //tukaj bi bila koda, ki pridobiva podatke iz service-a return "Podatki o osebi iz Service Repoitory" } } public class CSVRepository : IPersonRepository { public string VrniPodatke() { //tukaj bi bila koda, ki pridobiva podatke iz CSV datoteke return "Podatki o osebi iz CSV Repoitory" } }
Ok. sedaj imamo implementirane repozitorije. Imejmo razred RepositoryManager s konstruktorjem v katerega podamo želeni repozitorij, smo s tem uporabili Constructor Injection (eden od principov in najbolj pogost v Dependency Injection). Ker si pri implementaciji uporabil Interface bo konstruktor razreda RepositoryManager sprejel parameter tipa IPersonRepository, torej razred RepositoryManager ne bo imel odvisnosti (dependency) na razrede CSVRepository , ServiceRepository in SQLRepository ampak samo na interface IPersonRepository. V razredu RepositoryManager imejmo metodo PridobiPodatke, ki go klicala metode iz repozitorija, glede na to kateri repozitorij je bil določen v konstruktorju. Tako si razsklopil svojo kodo. Če bo potrebno pridobivati podatke iz baze, boš v ta parameter podal instanco razreda SQLRepository in pridobival boš podatke iz baze, če boš podal instanco razreda CSVRepository, boš pridobival podatke iz CSV datoteke ipd...
Primer implementacije tega razrade bi bil:
public class RepositoryManager { protected IPersonRepository Repository; public RepositoryManager(IPersonRepository repository) { Repository = repository; } public string PridobiPodatke() { return Repository.VrniPodatke() } }
Recimo konzolna aplikacija, ki uporabi vse našteto bi izgledala takole:
class Program { static void Main(string[] args) { var repoManager = new RepositoryManager(new CSVRepository()); //uporabil si constructor Injection Console.WriteLine(repoManager.PridobiPodatke()) } }
Kot vidiš si na lep in "clean" način skodiral aplikacijo. V konkretnem primeru zgoraj, si uporbil CSVRepository in boš pridobil podatke iz CSV datoteke. Če si sedaj šef izmisli da moraš pridobiti podatke še iz XML datoteke, boš izdelal razred XMLRepository, ki implementira interface IPersonRepository in boš v svoji aplikaciji spremenil samo tole vrstico:
var repoManager = new RepositoryManager(new XMLRepository());Z uporabo DI (Dependency injection) je postalo tvoja koda naenkrat lažja za vzrdževat, Tudi te ne zanima posamezna implementacija ker morajo vsi repozitoriji implementirati metodo VrniPodatke, te ne zanimajo te podrobnosti. Vse te zadeve si ovil v metodo PridobiPodatke, ki jo na vseh mestih kličeš enako. Če nebi uporabil DI, bi se verjetno lotil "štrikanja z raznimi IF stavki in instanciral, vse povprek ter glede na IF uporabljal določene implementacije, tako pa si uporabil DI in si olajšal življenje.
Še bolj si lahko olajšaš življenje, če uporabiš t.i. DI Container-je. To so implementacije posameznih tehnik DI in ti omogočajo, da to kar si ti naredil z vrstico
var repoManager = new RepositoryManager(new CSVRepository());prepustiš Containerju da neredi namesto tebe. Ti pri uporabi containerja definiraš naj se vse IPersonRepository objekti instancirajo kot CSV repository. DI Containerjov je več. v .NET svetu je eden znanih Unity. Če bi uporabil Unity bi lahko zapisal tudi takole:
IUnityContainer Container; static void Main(string[] args) { Container = new UnityContainer(); Container.RegisterType<IPersonRepository, CSVRepository> var repoManager = new RepositoryManager(Container.Resolve<IPersonRepository>()) }Torej kjerkoli v kodi kjer uporabim
Container.Resolve<IPersonRepository>()bom dobil CSVRepository instanco. Ena izmed dobrih lastnosti Unity containerja je tudi ta da lahko v XML datoteki podam konfiguracijo, in rečem v IPersonRepository se naj resolva v CSVRepository. Primer konfiguracije v app.config datoteki za naš primer bi bil:
<unity> <container> <register type="IPersonRepository" mapTo="CSVRepository"> </register> </container> </unity>
To pomeni, da bi Unity v Run-Time poskrbel da se bodo vsi IPersonRepository resolvali v CSVRepository. Konkretno ti to omogoča, da lahko sedaj svojo aplikacijo namestiš nekomu, ki ima podatke v SQL strežniku in ti ne bo potrebno ponovno prevesti aplikacije in uporabiti ustrezen repozzitorij, spremenil boš samo konfiguracijo v XML v
<register type="IPersonRepository" mapTo="SQLRepository">in bo aplikacija uporabljala SQLRepository razred za pridobivanje podatkov.
Ufff, dolg post sem napisal, pa saj sem rekel, da ne gre to na kratko razložit :) Upam, da ti je sedaj kaj bolj jasno, drugače pa kaj vprašaj :)
Edit: typos
Zgodovina sprememb…
- spremenil: Iluvatar ()
Vredno ogleda ...
Tema | Ogledi | Zadnje sporočilo | |
---|---|---|---|
Tema | Ogledi | Zadnje sporočilo | |
» | GitHub PomočOddelek: Pomoč in nasveti | 5669 (3931) | BivšiUser2 |
» | [C++] Enosmerno povezan seznamOddelek: Programiranje | 3544 (1726) | lebdim |
» | Drop down list in pre-selected opcijaOddelek: Izdelava spletišč | 561 (478) | neoserv |
» | pomoc pri skladuOddelek: Programiranje | 1320 (1245) | NoUse4AName |
» | Problem pri uporabi niti (threade) C++Oddelek: Programiranje | 1041 (896) | rabbit-zek |