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

Nejsem schopný dopsat článek o Continuous Integration

07.50 - 14. června 2011 | Moje práce

Je třeba si to přiznat. Nedokážu vše. Např. nedokážu odpřednášet nebo napsat článek o Continuous Integration. Je to škoda, protože si myslím, že toho o tom víc celkem dost, dlouho to praktikuju a tak, ale prostě je nad moje síly dát tomu lepší formu než konzultaci či workshop.

Každopádně jsem nad tím článkem strávil spoustu času a byla by škoda, kdyby se nikdy nedostal k očím dychtivých čtenářů. Proto jsem se rozhodl, že článek uvolním pod licencí Creative Commons, nabídnu ho ke stažení, těm co jim nevadí špatná forma. A pokud se najde dobrá duše, která se rozhodne článek dopsat/zeditovat a publikovat na zdrojáku, nechť tak učiní. Jen musí zachovat licenci a uvést mé jméno jako spoluautora. :) Toť pro dnešek vše.

Jo a tady je ten zázrak.

Přepínání featur a větvení abstrakcí

09.01 - 11. června 2011 | Moje práce

Správa několika verzí aplikace vždy přináší komplexitu, která zvyšuje náklady na vývoj i na údržbu. Běžnou praktikou je, že se zdrojové kódy v repositoři větví (branching) v určitých Milestonech, které se potom stabilizují a po releasu udržují, zatím co v hlavní větvi (trunk, main) se pokračuje ve vývoji. S příchodem DVCS se rozmohlo lokální větvení po featurách, čímž se různorodost verzí aplikace ještě víc rozrostla.

Naproti tomu tu máme agilní praktiky jako je Continuous Integration a Continuous Delivery, které nám říkají, že všichni commitují často do hlavní větve, čímž se předchází integračním problémům. Continuous Delivery navíc říká, že „aplikace má pouze jedinou verzi – tu aktuální.“ Jak to ale udělat, když rozsah některých featur přesahuje rámec sprintu? Ano, můžeme udělat feature branch, ale tím porušujeme pravidlo, že všechno je v hlavní větvi! Poslední verze TeamCity sice podporuje workflow s personal buildy vůči feature branchi, ale to je integrační smell.

Branch by abstraction

Existuje lepší řešení! Proč nevyužít technik OOP jako je polymorfismus a IoC? Je to krapet složitější, než branchování v repositoři, ale zároveň nás to donutí zamyslet se nad architekturou aplikace a ve výsledku bude IMO lepší. V aplikaci můžeme mít nekolik implementací požadované funkcionality, které jsou následně injektovány na základě konfigurace. Některé mohou být stabilní a používat se v produkčním prostředí, zatímco u vývojáře poběží aktuální edge implementace, na které se pracuje.

Takže implementaci kódu máme vyřešenou, ale jak na to v uživatelském rozhraní?

Feature toggles

Pokud naše UI není přímo generováno aplikačním kódem, kde by se dala využít předchozí technika, musíme sáhnout po nečem trochu jiném – přepínačích featur. Začneme tím, že každá feature má svůj jedinečný identifikátor. Každá featura je zařazena do uživatelského rozhraní s tímto identifikátorem a na základě konfigurace je opět rozhodnuto, zda dojde k jejímu zobrazení uživateli nebo ne. Takto se dají nejen snadno odříznout nestabilní featury z produkčního prostředí, ale dá se to využít i k takovým věcem, jako je A/B testování, čí postupné spouštění featury do produkce, jak to znáte z oblíbených služeb jako je Facebook nebo Twitter. ;)

Lepší události v C#

14.11 - 10. května 2011 | ASP.NET 2.0

Když se nedávno ptali Anderse Hejlsberga na to, co by nejradši v C# změnil nebo udělal jinak, kdyby mohl, odpověděl, že by zrušil události. Přesněji, že by je spojil s konceptem vlastností. Na první pohled jsou totiž vlastnosti a události celkem podobné, ale na druhý zjistíte, že události mají poměrně složitou syntaxi a vyžadují psát ne moc pěkný kód.

Dřív (C# 1.0) to asi o moc líp udělat nešlo, ale dnes tomuto přání můžeme plně vyhovět. Stačí události vystavovat jako vlastnosti typu IObservable<T>. Toto rozhraní je jednou z novinek BCL .net 4.0 a hlavně reaktivních extenzí (Rx.net).

Pokud píšete nový kód, zvažte užití IObservable<T> namísto událostí. Např. F# má události takto řešené nativně.

Ze starého na nový

Vezměme si ukázkovou deklaraci události v C#:

Dříve jsme museli definovat vlastní typ delegáta události:

public delegate void ChangedEventHandler(object sender, EventArgs e);

Pak vystavit událost tohoto typu:

public event ChangedEventHandler Changed;

A nakonec někde v kódu událost odpálit:

if (Changed != null)
  Changed(this, e);

Nehledě na podivné přetížení operátoru pro zavěšení ovladače události:

List.Changed += new ChangedEventHandler(ListChanged);

Nové verze jazyka a BCL nám přinesly mnohá usnadnění od EventHandler<TEventArgs>, přes automatického odvození typu delegáta až po lambda výrazy. To však nic nezměnilo na samotné koncepci událostí…

A teď si to tedy přepišme na sjednocený model vlastností a událostí s užitím IObservable<T>:

readonly Subject<Unit> changed = new Subject<Unit>();

public IObservable<Unit> Changed {
  get { return changed; }
}

V kódu událost vyvoláme následovně:

changed.OnNext(new Unit());

A ke zpracování události se přihlásíme takhle:

List.Changed.Subscribe(ListChanged);

Krom čistšího kódu (všimněte si absence defenzivního kódu a přetěžování operátorů, které na první pohled nemusí být každému jasné, co dělá) máme i spoustu dalších výhod, které přináší reaktivní programování.

PS. Třidy Subject<T> a Unit nejsou součástí BCL, ale zmiňovaných Rx.net. Do projektu si je přidáte pomocí nuget příkazu Install-Package Rx-Main.

Volme správné názvy!

14.59 - 8. dubna 2011 | ASP.NET 2.0

Ovládat dokážeme pouze věci, které dokážeme pojmenovat. Pokud něco dokážeme pojmenovat, můžeme se na to domluvit i s ostatními a komunikace je počátkem vývoje. Nebo tak to nejspíš bylo u lidí. ;)

“There are only two hard things in Computer Science: cache invalidation and naming things” [Phil Karlton]

Ano jedním z nejtěžších problémů v softwarovém vývoji je správně pojmenovat věci. Ostatně to není problém pouze softwarového vývoje. :) Přijít s dobrým jménem vyžaduje zkušenost a dobré vyjadřovací schopnosti.

Každý programátor dokáže napsat kus kódu, kterému rozumí počítač, ale jen málo z nich dokáže napsat kód, kterému budou rozumět i lidé (ať někdo jiný nebo po čase on sám). A volba správných názvů přímo vede k dobrému porozumění kódu!

Základní pravidla pro psaní srozumitleného kódu

  1. Nepsat kód v jiném národním jazyce než je angličtina (většina programovacích jazyků má klíčová slova anglická).
  2. Používat popisné názvy.
  3. Nepoužívat kryptické kódování alá maďarská notace.

Pravidla pro pojmenování proměnných

Proměnná je předmět a tudíž by to měl její název reflektovat. Nejčastěji by názvy proměnných měla být podstatná jména (případně s přívlastky.) Výjimkou jsou boolovské proměnné a delegáty. Proměnné typu bool by se měly dát číst jako predikáty:

bool isEnabled = false;
bool hasChildren = true;

Pro delegáty platí většinou pravidla stejná jako pro tvorbu názvů metod (viz. dále.)

Délka názvu proměnné by měla reflektovat scope v jakém se nachází. Pokud mám proměnou v lambda výrazu, klidně může být jednopísmenková. Např. x => x * x. Podobně je na tom proměnná pro index ve smyčce nebo prvek foreach cyklu. (Pokud tělo cyklu není delší než 4 řádky.)

V případě proměnných z působností v metodách bychom už měli používat celá slova či popisná sousloví. Pokud máme proměnné s větším polem působnosti, jako jsou parametry metod nebo členské proměnné, měly bychom je pojmenovat delším popisnějším názvem.

readonly IArticlesRepository articlesRepository;

Pravidla pro pojmenování metod

U metod je to přesně naopak než u proměnných. Metody vyjadřují nějakou akci a proto by měly začínat slovesem. Veřejné metody by měly mít název krátký a jasný. Jedno písmenkové metody patří možná tak akorát do globálního scope (viz. jQuery ;) ale všichni víme, že globální scope je špatný, že ano?) Proč krátké? Protože je pravděpodobné, že je budeme používat často a nejspíš nejen my, ale i někdo jiný. Název však nesmí obsahovat zbytečné implementační detaily (ty by měly být v dokumentačním komentáři). Pěkným příkladem je třeba File.Open.

Naproti tomu privátní metody by měly mít popisné názvy dlouhé i několik slov nebo to může být krátká věta. Nahrazují nám totiž rychle zastarávající komentáře. Nebojte se často používat refactoring Extract Method na menší logické kusy kódu a pěkně je pojmenujte. Kód se vám bude číst mnohem líp!

Pravidla pro pojmenování tříd

Pro třídy platí podobná pravidla jako pro metody, akorát že třídy jsou opět podstatná jména. Privátní třídy pojmenováváme popisně, veřejné stručně a výstižně. U abstraktních tříd používáme abstraktní jména. Potomci pak toto jméno rozšiřují o konkrétní přívlastky.

Závěr

Volba správného jména je velice důležitá, protože, když se na váš kód podíváte pozorně, zjistíte, že jde v podstatě o variace neustále se opakujících konstrukcí a význam jim dodávají právě jména.

K čemu je dobré refaktorovat cykly do LINQ dotazů?

07.30 - 26. prosince 2010 | ASP.NET 2.0

V posledním zápichu jsem ukázal, jak refaktorovat cyklus na LINQ. Boris v komentářích poukázal na to, že výsledný kód nemusí být zrovna optimální, co se týče konzumace zdrojů. Já myslím, že je to malá cena, která je vyvážena obrovskou flexibilitou.

Nejprve se vraťme k ukázkovému kódu. Ještě trochu jsem ho přepsal, aby byl výsledek srozumitelnější:

static void ParseRouteData(Regex regex, Match match, RouteData data) {
  var groups = match.Groups.Cast<Group>();
  var groupNames = regex.GetGroupNames();

  var matchingPairs =
    from item in groups.
      Zip(groupNames, (group, name) => new {
        Pair = CreatePair(name, group.Value),
        IsMatch = group.Success,
      })
    where item.IsMatch && IsValidKeyAndValue(item.Pair)
    select item.Pair;

  matchingPairs.Do(SetRouteData(data));
}

static KeyValuePair<string, string> CreatePair(string key, string value) {
  return new KeyValuePair<string, string>(key, value);
}

static bool IsValidKeyAndValue(KeyValuePair<string, string> pair) {
  return IsNamedKey(pair) && IsNotEmptyValue(pair);
}

static bool IsNamedKey(KeyValuePair<string, string> pair) {
  return !(string.IsNullOrEmpty(pair.Key) || char.IsNumber(pair.Key, 0));
}

static bool IsNotEmptyValue(KeyValuePair<string, string> pair) {
  return !string.IsNullOrEmpty(pair.Value);
}

static Action<KeyValuePair<string, string>> SetRouteData(RouteData data) {
  return pair => data.Values[pair.Key] = pair.Value;
}
  1. Upravil jsem název funkce. Nyní už neiterujeme, ale po odhalení, co funkce vlastně dělá, můžeme říct, že parsuje routovací data. Odstínili jsme se od implementačního detailu. Jsme více deklarativní.
  2. Změnil jsem lambda based zápis na query like zápis. Ten se stejně na lambda based zápis převede kompilátorem, ale odstraní se zbytečný šum.
  3. Přibyly dvě další funkce. CreatePair jsem zavedl pro zkrácení řádku, aby se nemuselo tady na blogu scrollovat doprava, ale opět nás odstiňuje od zbytečné informace (implementační detail).
  4. Oddělil jsem dotaz a jeho samotné vykonání.

Proč tedy LINQ?

LINQ nám umožňuje zapisovat programy deklarativně. To znamená, že píšeme, čeho chceme dosáhnout, ne jak toho chceme dosáhnout. To je obrovská výhoda! Teď zapomeňme na to, že filtrujeme jednotky vyparsovaných hodnot. Mějme jich tisíce nebo rovnou milióny! Pokud potřebujeme výsledek rychle, jsme v imperativním případě namydlený. Sice je výkonostně optimálnější než ten deklarativní, ale pouze v případě, že máme k dispozici jediný stroj s jediným jádrem CPU.

Pokud máme jader víc nebo celý cluster, můžeme vykonání LINQ dotazu distribuovat na více jader (pomocí TPL) nebo do celého clusteru (pomocí Dryad). Více jader je u dnešních počítačů už standard a tak mikrooptimalizace jsou většinou ke škodě.

Samozřejmě existují výjimky, jako jsou např. mobilní zařízení, kde není výkonu nazbyt. Otázkou je, zda raději nepřenechat výpočetně složité operace na serverech? ;)

Pryč se stromečky! Nyní interaktivně.

07.35 - 22. prosince 2010 | ASP.NET 2.0

Včera jsme si předvedli jak pomocí několika základních refaktoringů snížit cyklomatickou komplexitu, zpřehlednit tak kód a snížit riziko výskytu chyb. Dnes se posuneme ještě o kousek dál a zbavíme se nadbytečného imperativního kódu.

LINQuj, LINQuj

Když jsem dorefaktoroval metodu IterateMatchingPathGroups do poslední verze s guardy, hned jsem viděl, že tohle je jasný LINQoký dotaz přepsaný imperativně. Původně jsem ho chtěl přidat do včerejšího článku, ale pak jsem zjistil, že nejde úplně o triviální dotaz a že to nechám na pozorném čtenáři na rozvinutí v komentářích.

Netrvalo dlouho a na twitteru mi psal Tomáš Petříček, že by tato metoda šla přepsat pomocí LINQu. Inu, funkcionální srdcař se nezapře. :) Tak mi to nedalo a píšu tenhle druhej díl.

Interaktivní extenze na scénu!

Jak už jsem psal, nejde o úplně triviální LINQ dotaz, protože tu potřebujeme mapovat dvě kolekce na sebe. Ve čtvrté verzi .net frameworku byl přidán monad Zip, který dělá přesně to, co potřebujeme. (Pokud používáte .net 3.5, najdete tento v knihovně System.Interactive v rámci Rx.net.) Tady je výsledný kód, který si popíšeme dále:

static void IterateMatchingGroups(Regex regex, Match match, RouteData data) {
  GroupCollection groups = match.Groups;
  string[] groupNames = regex.GetGroupNames();

  groups.Cast<Group>().
    Zip(groupNames, (group, name) => new {
      Pair = new KeyValuePair<string, string>(name, group.Value),
      IsMatch = group.Success,
    }).
    Where(x => x.IsMatch).
    Select(x => x.Pair).
    Where(IsNamedKey).
    Where(IsNotEmptyValue).
    Do(SetRouteData(data));
}

static bool IsNamedKey(KeyValuePair<string, string> pair) {
  return !(string.IsNullOrEmpty(pair.Key) || char.IsNumber(pair.Key, 0));
}

static bool IsNotEmptyValue(KeyValuePair<string, string> pair) {
  return !string.IsNullOrEmpty(pair.Value);
}

static Action<KeyValuePair<string, string>> SetRouteData(RouteData data) {
  return pair => data.Values[pair.Key] = pair.Value;
}

Nejprve si připravíme vstupní kolekce skupin a jejich názvů. Protože GroupCollection implementuje pouze negenerické IEnumerable použijeme extenzi Cast<TResult>, která vytvoří generickou kolekci, nad kterou už se dají použít LINQ monady. Dále obě kolekce sezipujeme a vytvoříme si pomocný anonymní objekt, který drží pár klíč-hodnota a informaci, zda byl úspěšný match.

Odfiltrujeme si neúspěšné matche a dál už potřebujeme jen pár klíč-hodnota, tak si ho vyselectujeme. Odfiltrujeme páry, které nemají klíč nebo je klíčem číslo, a pak ještě ty, které nemají hodnotu. Tím jsme sémanticky nahradili guard podmínky z cyklu předchozí verze. A dostali jsme se k tomu, co vlastně chceme udělat, tj. přidat nalezené hodnoty do kolekce routovacích dat. K tomu slouží monad Do z interaktivních extenzí.

Pokud zrovna nechcete používat skvělou knihovnu Rx.net, můžete monad Do nahradit zápisem ToList().ForEach nebo AsParallel().ForAllTPL.

A to je pro dnešek vše. Nějaká dotazy, výtky a tak? :)

Pryč se stromečky!

16.44 - 21. prosince 2010 | ASP.NET 2.0

Blíží se Vánoce, čas stresu a shonu za nejdražšími Vánočními dárky, pro naše nejdražší. Čas, kdy se ve velkém mordují kapři (navíc na veřejnosti, no fůj!) a kácí se stromky. Ano, ty které potom ověsíme nejrůznějšími koulemi a řetězy, aby pak několik týdnů opadávalo jehličí a vůbec. O tomhle ale psát nechci. :)

Dneska se podíváme na jiný úkaz. A to na problematiku zanořování kódu, který ve výsledku vypadá jako stromečky. (Pokud jste takový kód ještě neviděli, což pochybuju, tak ukázku najdete třeba tady v sekci Dark Magic.)

V čem je problém?

Možná si říkáte, „co to ten Roubíček zase vymejšlí? Vždyť takovej kód je přece v pořádku,“ (tj. dělá, to, co má.) „A navíc nemusíme chodit do lesa, protože máme stromečky pořád na očích!“ To je fakt. Jenže, ruku na srdce, dokážete odhadnout na první pohled, co takový kód vlastně dělá? Moc asi ne. A proč? Protože pět úrovní zanoření (nepočítám namespace, class ani metodu) je už trochu moc.

Jak moc? No hrozně moc. Fakt! Ono se to totiž dá krásně změřit, např. pomocí metriky Cyclomatic complexity.

Když se podíváme na tuto metriku u ukázkového příkladu, zjistíme, že metoda GetRouteData dosahuje hodnoty 17. Co to znamená? Když si přečtete odkazovaný popis metriky, dozvíte se také, že její hodnota určuje míru možného výskytu chyb. Čím větší je cyklomatická komplexita, tím více toho modul dělá a zvyšuje se riziko přehlédnutých chyb. Doporučuje se mít komplexitu do deseti a tady tuto hodnotu překračujeme takřka dvojnásobně.

It smells like refactoring time!

Refaktorovat, refaktorovat, refaktorovat

Jednoduše jsme pomocí metriky odhalili kandidáta, kde budeme kácet stromy. Když se podíváme pozorně na ukázkový příklad, můžeme si všimnout bloků kódu, které začínají komentářem. Budu se opakovat, ale nepište zbytečný kód! Tohle jsou jasné samostatné metody. Použijeme tedy refaktoring Extract method. Po extrahování čtyř metod, už se nám metoda vejde na jednu obrazovku a cyklomatická komplexita spadne na trojku. Takřka na jednu šestinu!

Huh. A to je teprve začátek.

Po prvním kole vypadá výsledek asi tak:

public override RouteData GetRouteData(HttpContextBase httpContext) {
  // Build regex
  domainRegex = CreateRegex(Domain);
  pathRegex = CreateRegex(Url);
  // Request information
  string requestDomain;
  string requestPath;
  RequestInformation(httpContext, out requestDomain, out requestPath);
  // Match domain and route
  Match domainMatch = domainRegex.Match(requestDomain);
  Match pathMatch = pathRegex.Match(requestPath);
  // Route data
  RouteData data = null;
  if (domainMatch.Success && pathMatch.Success) {
    data = new RouteData(this, RouteHandler);
    AddDefaultsFirst(data);
    IterateMatchingDomainGroups(domainMatch, data);
    IterateMatchingPathGroups(pathMatch, data);
  }
  return data;
}

Není to žádná nádhera, ale je mnohem přehlednější, než originál. A v řeči čísel:

Rozplétáme podmínky

Tak, snížili jsme komplexitu metody, ale tiše nám narostla komplexita třídy. Klidně můžeme pokračovat v cestě, to zvládneme později. Teď se zaměříme na ty velký šestky. Na první pohled cyklus se spoustou zanořených podmínek. Pryč s nimi! Teď přijde na řadu refaktoring Flatten Conditional a z vše objímajícího ifu uděláme jen guarda. Dále tu mám dva zanořený ify? Proč, když je můžeme logicky spojit operátorem &&? Směle do toho! Pro změnu použijeme refaktoring Combine Conditionals a vytvoříme jednu dlouhou a ošklivou podmínku…

Čas na refaktoring Extract method a podmínku pěkně smysluplně pojmenovat. Po pár magických kombinacích kláves se nám z týhle zrůdičky:

private static void IterateMatchingPathGroups(Match pathMatch, RouteData data) {
  for (int i = 1; i < pathMatch.Groups.Count; i++) {
    Group group = pathMatch.Groups[i];
    if (group.Success) {
      string key = pathRegex.GroupNameFromNumber(i);
      if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0)) {
        if (!string.IsNullOrEmpty(group.Value)) {
          data.Values[key] = group.Value;
        }
      }
    }
  }
}

Stane opět poměrně snadno pochopitelný kód:

private static void IterateMatchingPathGroups(Match pathMatch, RouteData data) {
  for (int i = 1; i < pathMatch.Groups.Count; i++) {
    Group group = pathMatch.Groups[i];
    if (IsNotMatch(group))
      continue;

    string key = pathRegex.GroupNameFromNumber(i);
    if (IsInvalidKeyOrValue(group, key))
      continue;

    data.Values[key] = group.Value;
  }
}

Navíc, když se teď na ten kód podíváme, zjistíme, že se ty původní šestky (z kterých už jsou čtyřky) liší jen v tom, že používají každá jinou členskou proměnou, naštěstí stejného typu. Tak jdeme zase refaktorovat. :) Nejprve si tedy tuto proměnou uložíme lokálně na začátku metody a ze zbytku extrahujeme metodu novou s jedním parametrem navíc.

U druhý čtyřky už to dělat nemusíme, páč výsledný extrakt z té první je plně vyhovující našim potřebám a tak ho zavoláme jen s jiným parametrem. Ze dvou čtyřek máme jednu a dvě jedničky. A komplexita třídy se nám vrátila k původní hodnotě. Ano, máme tu teď spoustu malých metod, které ale mají jasný účel. A když se podíváme blíž, zjistíme, že můžeme úplně v klidu některé z nich extrahovat no úplně nové třídy. Ale to už je na jinou pohádku.

Stromečky jsou totiž fuč. :)

PS. Zmíněné refaktoringy a vizualizace metrik jsou součásti skvělého produktu Refactor Pro! nebo samotné refactoringy v neplacené verzi Code Rush Xpress.

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í. :)

Můj pohled na F# se mění

14.04 - 9. listopadu 2010 | ASP.NET 2.0

Za posledních pár měsíců se mi celkem změnil pohled na F#. Ještě do nedávna jsem si myslel, na co další jazyk, když C# zvládá funkcionální paradigma taky obstojně?

Ale pak jsem psal v C# pár aplikací, kde docházelo k masivnímu přelívání dat a jejich transformaci. V podstatě jednoduchý funkcionální kód jsem obaloval poměrně košatou objektovou strukturou. A pak jsem začal číst knihu Expert F# 2.0 a měl pár pěkných aha momentů. A začal jsem vidět, jak jsem si mohl spoustu práce ušetřit…

Teoreticky. :)

Asynchroní načítání featur v jQuery

11.21 - 12. září 2010 | Webdesign

K optimalizaci rychlosti načítání stránek se můžeme postavit různě. Někdo to neřeší, někdo snižuje počet požadavků jednou velkou haldou scriptů a někdo načítá featury asynchronně.

Je to celkem snadné. Místo jednoho velikého scriptu s jQuery, všemi pluginy a vlastními scripty, připojíme do šablony webu scripty dva. Jeden bude linkovat jQuery z CDN druhý bude bootstrapper, který bude asynchronně donačítat scripty pro featury na dané stránce:

<script type="text/javascript"
  src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js">
</script>
<script type="text/javascript" src="/scripts/bootstrapper.js"></script>

Jak vypadá takový bootstrapper? Následovně:

jQuery(function($) {
  initCalendar($);
});

function initCalendar($) {
  var cal = $('.calendar');
  if (cal.length == 0) return;
  $.getScript('/scripts/jquery.calendar.js', function() {
    cal.calendar();
  });
}

Jak to funguje?

Když máme připravený DOM, jQuery spustí naši inicializační funkci pro kalendáře. Pokud na stránce žádné kalendáře nemáme, tak skončíme. Nemá cenu načítat script s kalendářem. Pokud na stránce, nějaké kalendáře jsou, tak načteme potřebný script a zaregistrujeme funkci, která se spustí po zavedení scriptu. V tomto případě obohatí stránku o pěkné kalendáře. :)

Zbavili jsme se tak blokujících čekání na načtení scriptů, načítáme pouze dva scripty, z toho první je jistě už v lokální cache prohlížeče. Na zbylé scripty nemusí prohlížeč čekat a stránka se vykreslí celkem rychle bez záseků. Jednotlivá vylepšení stránky se donačtou záhy. Uživatel nic nepozná, jen bude mít plynulejší prožitek.

A to je vše, milé děti.