Klávesové zkratky na tomto webu - rozšířené Na obsah stránky

Service Locator

11.44 - 30. května 2009 | ASP.NET 2.0

Znáte vzor Service Locator? Pravděpodobně jste s ním setkali nespočetněkrát aniž byste o tom věděli.

Update

Service locator je code smell. Následující řádky jsou zajímavé jako programátorské cvičení, nedoporučuji ho však používat v praxi. Třída by měla mít co nejméně závislostí a pokud chceme zredukovat počet parametrů konstruktoru, není následující řešení řešením vhodným! Měli bychom přemýšlet o změně návrhu.


Service Locator je návrhový vzor, který je součástí techniky dependency injection / inversion of control. Jeho úkolem je dodat hotové instance služeb na vyžádání. Můžeme si ho představit nějak tak:

public interface IServiceLocator {
  TService GetService<TService>();
  IEnumerable<TService> GetServices<TService>();
}

Jeho úkolem je poskytnout službu daného typu nebo všechny služby, které splňují danný kontrakt. K čemu je to dobré? Dobré je to především pro konfigurovatelné a rozšiřitelné aplikace. Řekněme, že budu chtít do svého redakčního systému přidat možnost oznámení na e-mail, když někdo zadá komentář. První řešení bude nejspíš to přímočaré:

public class CommentsController : Controller {
  public ActionResult AddComment(Comment comment) {
    // validace a persistence komentáře

    var notificator = new EmailNotificator();
    notificator.send(new CommentNotification(comment));

    return Json(comment);
  }
}

Proč nepřidat možnost odesílání SMSek? Tak jo:

public class CommentsController : Controller {
  public ActionResult AddComment(Comment comment) {
    // validace a persistence komentáře

    var emailNotificator = new EmailNotificator();
    emailNotificator.send(new CommentNotification(comment));

    var smsNotificator = new SmsNotificator();
    smslNotificator.send(new CommentNotification(comment));

    return Json(comment);
  }
}

Skvěle, teď ještě posílání na Twitter nebo na FriendFeed a akce nám krásně roste… :) Takže se oprostíme od toho, že dopředu víme, kudy všudy se chceme nechat informovat o nových komentářích a využijeme Service Locator:

public class CommentsController : Controller {

  private readonly IServiceLocator _serviceLocator;

  public CommentsController(IServiceLocator serviceLocator) {
    _serviceLocator = serviceLocator;
  }

  public ActionResult AddComment(Comment comment) {
    // validace a persistence komentáře

    var newCommentNotification = new CommentNotification(comment);
    var notificators = _serviceLocator.GetServices<INotificator>();
    notificators.Each(notificator => notificator.send(newCommentNotification));

    return Json(comment);
  }
}

Tím jsme také vyřešili problém vznikající při constructor injection a to rostoucí počet parametrů konstruktoru s rostoucími závislostmi. Protože service locator nám dokáže obstarat potřebné služby, nemusíme je injektovat zvlášť.

Ještě se vrátím k poznámce v úvodu, kde jsem psal, že jste se určitě s tímto vzorem setkali, aniž byste si toho byli vědomi. O co jde? O Singleton! :) Singleton je speciální případ service locatoru, který vrací službu jediného typu s řízeným životním stylem jedináčka.

To je všechno pěkné, ale jak tedy service locator ví, jak ty služby získat a jakej mají životní styl? Pokud používáte IoC kontejner, odpověd je snadná: service locator si udělám jako fasádu nad kontejnerem a tu do něj zaregistrujeme. Pokud žádný IoC kontejner nepoužíváte, tak si honem nějaký sežeňte! :)

Komentáře RSS

  1.  

    dkl

    13.36 - 30. května 2009 | #

    Další variantou jsou singleton registry objekty, které mají reference na jednotlivé služby, např.

    public class WebLayerRegistry { public static WebLayerRegistry Instance { get; set; }

    public IMailNotificator MailNotificator { get; set; } public IAuthorization­Service Authorization­Service { get; set; } ... }

    public class CommentsController : Controller {

    // no extra field // no special constructor

    public ActionResult AddComment(Comment comment) { // validace a persistence komentáře

    WebLayerRegis­try.Instance.Ma­ilNotificator­.Send(new CommentNotifi­cation(commen­t));

    return Json(comment); } }

    Výhoda je, že IoC kontejner potřebuju jenom při startu aplikace na „nakonfigurování“ registry objektu. Všechny ostatní objekty můžu vytvářet úplně obyčejně pomocí konstruktoru (nepotřebuju závislost na IoC framework).

    Inspiraci jsme vzali odsud: http://martin­fowler.com/…jec­tion.html#…

    BTW zajímalo by mě jaké zpomalení představuje vytváření controllerů v MVC pomocí IoC kontejneru. Nikdy jsem se nedostal k tomu abych to nějak změřil.

  2.  

    Aleš Roubíček

    13.59 - 30. května 2009 | #

    [1] dkl: Databázové operace jsou řádově pomalejší :)

  3.  

    Borek

    17.30 - 2. června 2009 | #

    Zajímavý článek. Service Locator se většinou používá jako alternativa k Dependency Injection, protože k získávání svých dependencies používají rozdílné techniky (injection vs. lookup). Většinou tíhneš k jednomu nebo druhému přístupu, použití obou dohromady je poměrně řídké, i když, jak ukazuješ, zdaleka ne nemožné.

    Výhodu oproti constructor injection jsi napsal, má ale i nevýhodu: není na první pohled jasné, jaké jsou závislosti tvého controlleru. Rovněž může použití Service Locatoru vést k tomu, že ti je jedno, kolik má controller závislostí, protože jsou schované za jedním konstruktorovým parametrem. Při normální contructorové injection bys musel trochu pořešit design aplikace, protože moc závislostí může být nepěkný code smell.

    Jen pro doplnění :)

  4.  

    Aleš Roubíček

    18.22 - 2. června 2009 | #

    [3] Borek: Tohle je jen demo code a nástin možného řešení, jak to řešim v real life aplikaci si můžem ukázat zítra :)

Místo pro tvůj názor

Povinné je jméno a komentář, z e-mailu se rozpoznají Gravatary.
Komentář je formátován pomocí Texy! syntaxu.
Například: **tučný text**, *kurzíva*, "text odkazu":adresa.
Internetové adresy jsou převáděny na odkazy.
Na komentáře se můžete odkazovat pomocí [číslo komentáře].

Nový komentář