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

Single Responsibility Principle in the wild

13.31 - 20. listopadu 2010 | ASP.NET 2.0

Tak jsem se tak koukal na poslední CloudCover, kde představovali novou službu Windows Azure AppFabric Caching. Služba je to jistě zajímavá a je jednou z možností jak zajistit session state affinity v Azure. Ale proč o tom píšu.

Součástí epizody byla i živá ukázka, jak přidat cachování do existující aplikace.

Teď to možná některé NHibernate znalé veterány zaskočí, ale v Microsoftím světě ORM si o něčem jako 2nd level cache můžete nechat zdát. Vůbec tam spoustu krásných věcí nemají, ale o tom třeba až jindy.

No prostě tam měli repository (používat repository, tam kde je LINQ, je už samo o sobě obskurní, ale budiž), která vypadala nějak tak:

public class ProductsRepository : IProductsRepository {

  public List<string> GetProducts() {

    List<string> products = null;

    NorthwindEntities context = new NorthwindEntities();
    var query = from product in context.Products
                select product.ProductName;

    products = query.ToList();

    return products;
  }
}

Mno, měli tedy respository produktů, která vracela názvy všech produktů přímo z databáze. No a pak se jali přidat cachování takového listu:

public class ProductsRepository : IProductsRepository {

  DataCacheFactory dcf;

  public ProductsRepository() {
    dcf = new DataCacheFactory();
  }

  public List<string> GetProducts() {

    List<string> products = null;

    DataCache dc = dcf.GetDefaultCache();

    products = dc.Get("products");

    if (products != null) {
      return products;
    }

    NorthwindEntities context = new NorthwindEntities();
    var query = from product in context.Products
                select product.ProductName;

    products = query.ToList();

    dc.Put("products", products);

    return products;
  }
}

Úžasné! Jenže tím jsme porušili princip jediné zodpovědnosti. Teď se nám repository účastní hned na dvou záležitostech – na své původní (načíst data z databáze) a nově na cachování načtených dat. Takže to smrdí. It smels like refactoring time!

Jak na to?

Cachování je věc hodná vlastního aspektu. Když se podíváme na výsledný kód, zjistíme, že jsme neudělali nic jiného než, že jsme odekorovali načítání dat logikou pro cachování. Tudíž zvolíme vzor dekorátor! Repository vrátíme do původního stavu, protože ten svůj účel splňuje a přidáme další třídu s jedinou zodpovědností:

public class CachedProductsRepository : IProductsRepository {

  const string ProductsKey = "products";

  readonly DataCacheFactory cacheFactory;
  readonly IProductsRepository repository;

  public CachedProductsRepository(DataCacheFactory cacheFactory, IProductsRepository repository) {
    this.cacheFactory = cacheFactory;
    this.repository = repository;
  }

  public List<string> GetProducts() {

    var cache = cacheFactory.GetDefaultCache();
    var products = cache.Get(ProductsKey);

    if (products == null) {
      products = repository.GetProducts();
      dc.Put(ProductsKey, products);
    }

    return products;
  }
}

No a máme to! Dokonce si teď můžeme vybrat, kdy chceme používat cache nebo sahat do databáze přímo. A to vše díky díky dodržování Open/Closed principle a jednoduché kompozici objektů, které dělají jen a pouze to, co mají. :)

Komentáře RSS

  1.  

    Augi

    16.43 - 20. listopadu 2010 | #

    Ty jsi hroznej vrstvič :)

  2.  

    Steves

    20.41 - 23. listopadu 2010 | #

    Je to skutečně dekorátor?, asi bych to nazval spíš proxy (zástupce). AOP frameworky, co dělají interception za běhu, podobné objekty taky nazývají jako dynamic proxy.

  3.  

    Aleš Roubíček

    22.10 - 23. listopadu 2010 | #

    Ano je to skutečně dekorátor. sám o sobě neudělá nic. Jen dekoruje injektovaný objekt o další funkcionalitu. A na venek se tváří stejně jako objekt, který dekoruje (má stejné rozhraní).

  4.  

    Petr

    17.04 - 24. listopadu 2010 | #

    Díky za pěkný článek.

    Můžeš trochu rozvést tu pznámku v závorce " (používat repository, tam kde je LINQ, je už samo o sobě obskurní, ale budiž)"

    Případně jak bys zajistil stejnou funkčnost bez repository?

    Díky

  5.  

    Aleš Roubíček

    20.33 - 25. listopadu 2010 | #

    Tak si vem definici Repository http://martinfowler.com/…ository.html a porovnej s IQueryable. De facto to samé…

  6.  

    Augi

    10.55 - 26. listopadu 2010 | #

    Jj, IQueryable je Repository, Context je UnitOfWork.

  7.  

    google

    00.01 - 26. prosince 2010 | #

    umím html, ale tady se fakt nechytám :-)

  8.  

    Petr

    18.53 - 26. prosince 2010 | #

    [5] Jo, tohle vím, ale jak přidat do linqu cachování, případně další vlastnosti?

  9.  

    Aleš Roubíček

    19.25 - 26. prosince 2010 | #

    K tomu je potřeba zavést abstrakci, například pomocí DAO a Query, nebo vlastní extenze nad IQueryable.

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ář