» »

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!

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/

abyssus ::

Hvala obema!

Spura ::

mihies je izjavil:

Zelo na kratko. DI/IOC (inversion of control) ti omogočata (lažje) testiranje (unit testing) in lažje menjavanje kosov aplikacije.

Tut ene besede nisi porabu za razlago kaj DI je, kar razlagas so prednosti DI.

mihies ::

Nisem napisal kaj to je, ker lahko pogledaš npr. na
Dependency injection @ Wikipedia
Bistvene prednosti sem pa napisal, al ne?
http://blog.rthand.com/
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.
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 ()

abyssus ::

@Iluvatar, res hvala za tole razlago! Mislim, da mi je stvar sedaj popolnoma jasna. :)


Vredno ogleda ...

TemaSporočilaOglediZadnje sporočilo
TemaSporočilaOglediZadnje sporočilo
»

GitHub Pomoč

Oddelek: Pomoč in nasveti
455734 (3996) BivšiUser2
»

[C++] Enosmerno povezan seznam

Oddelek: Programiranje
113552 (1734) lebdim
»

Drop down list in pre-selected opcija

Oddelek: Izdelava spletišč
5572 (489) neoserv
»

pomoc pri skladu

Oddelek: Programiranje
51332 (1257) NoUse4AName
»

Problem pri uporabi niti (threade) C++

Oddelek: Programiranje
101053 (908) rabbit-zek

Več podobnih tem