Na obsah stránky

Service Locator

Aleš Roubíček | | # permalink

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! :)

Našli jste v článku chybu? Máte námět na reportáž? Založte mi ticket.