Buzz words okolo dependency injection

Dependency Injection (DI), Dependency Inversion Principle (DIP), Inversion of Control (IoC), Service Locator. Víte jaký je mezi nimi rozdíl? Například na codedeproject.com si můžete přečíst pěkný článek o DI a IoC, ve kterém ale autor řadí Service Locator mezi možné implementace DI, zatímco Martin Fowler, popularizátor DI, to vidí jinak a zapravdu mu dávají i na Wikipedii. Troufám si říct, že podobných nepřesností se dá na internetu nalézt více. Nejdůležitější je nakonec znát a umět aplikovat principy a ne detaily v podobě nuancí mezi DI a DIP, ale proč si trochu neujasnit terminologii a vymýtit tak další buzz words ze svého slovníku.

Diagram vztahů mezi jednotlivými pojmy

Inversion of Control

Inversion of Control znamená, že tok programu je obrácený oproti klasickému procedurálnímu programovaní. Např. klasická procedurální konsolová aplikace

Console.WriteLine("Ahoj, jak se jmenujes?");
string name;
Console.ReadLine(name);
Console.WriteLine("Ahoj ", name);

se ve světě událostmi řízeného programování změní na

public Form() {
    this.button.Click += new EventHandler(ButtonClick);
}

void ButtonClick(object sender, EventArgs e) {
    MessageBox.Show("Ahoj " + this.nameLabel.Text);
}

Kontrolu nad tokem programu tedy převezme nějaký framework, v našem případě Windows.Forms, a uživatel mu pouze řekne, co se má provést během význačných událostí jako je kliknutí na tlačítko. Inversion of Control ale není jen o událostmi řízeném programování, je to obecný princip.

Dependency Inversion Principle

Dependency Inversion Principle je speciálním případem Inversion of Control. V tomto případě obracíme závislost mezi moduly programu, v OOP třídami, tak aby implementace high-level modulů nezávisela na konkrétní implementaci low-level modulů.

Klasickým příkladem může být známý repository pattern. High-level třídy přistupují do databáze pouze přes interface, který by měl navenek vypadat jako in-memory kolekce objektů, jsou tak odstíněny od konkrétních implementačních detailů jako např. od SQL dotazování.

Dependency Inversion Principle nám ale nic neříká o tom, jak high-level třídy získají instanci nějaké low-level třídy implementující požadované rozhraní. Na to můžeme použít Dependency Injection nebo Service Locator.

Service Locator

Myšlenkou tohoto postupu je, že máme globální objekt, který pro každý interface vrátí konkrétní implementaci. Pokud tedy potřebujeme zavolat nějakou low-level službu, řekneme Service Locatoru, aby nám dal instanci objektu, který implementuje její interface.

class AccountService : IAccountService {
   private IAccountRepository accountRepository;

   public AccountService() {
       this.accountRepository = ServiceLocator.GetAccountRepository();
   }
   // ...
}

Dependency injection

Oproti Service Locatoru, Dependency Injection odebírá high-level třídám zodpovědnost za získávání konkrétních instancí. Místo toho by měly tyto high-level třídy prostřednictvím svého veřejného rozhraní poskytnout informace o tom, jaké low-level služby potřebují, a o jejich instancování a složení dohromady se postará samostatná komponenta, často nazývaná IoC kontejner. Mezi nejpoužívanější způsoby, jak zveřejnit své závislosti, patří contructor injection a setter injection.

Constructor vs. setter injection

Constructor injection spočívá v tom, že třída požaduje své závislosti jako parametry konstruktoru.

class AccountService : IAccountService {
   private IAccountRepository accountRepository;

   public AccountService(IAccountRepository accountRepository) {
       this.accountRepository = accountRepository;
   }
   // ...
}

Při použití setter injection nabídneme své závislosti jako write-only (případně i RW) properties.

class AccountService : IAccountService {
   public AccountService() {}

   public IAccountRepository AccountRepository { set; private get; }
   // ...
}

IoC kontejner

IoC kontejner je takový „assembler“, který nám to potom všechno složí dohromady. Mezi nejznámější IoC kontejnery patří StructureMap nebo Unity přímo od Microsoftu. Tyto kontejnery podporují různé způsoby konfigurace, ve které je třeba jim sdělit, jaká konkrétní implementace se má použít pro ten a ten interface.

DI v praxi

I když použijeme Dependency Injection, stejně musí v našem programu existovat alespoň jedno místo, kde zavoláme zmiňovaný IoC kontejner, aby nám instancoval nějakou třídu a při tom vytvořil celý objektový graf a doplnil všechny závislosti. Takovým místem je např. controller factory v ASP.NET MVC.

    public class ControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(
                      RequestContext requestContext,
                      Type controllerType)
        {
             ServiceLocator.Current.GetInstance(controllerType) as IController;
        }
    }

    //příklad controlleru
    public class AccountService : Controller
    {
         private IAccountService accountService;

         public AccountService(IAccountService accountService) {
            this.accountService = accountService;
         }

         // ...
    }

Trackbacks & Pingbacks 2

  1. From PhpUnit v praxi | Steve blog on 24 Lis 2010 at 21.53

    [...] odkazovaného článku, neměl ponětí, ale teď si můžete přečíst můj další článek buzzwords okolo dependency injection, inversion of control a dalších na mém novém [...]

  2. From PhpUnit v praxi 2 | Steve blog on 24 Lis 2010 at 22.44

    [...] odkazovaného článku, neměl ponětí, ale teď si můžete přečíst můj další článek buzzwords okolo dependency injection, inversion of control a dalších na mém novém [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *