Na obsah stránky

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

Aleš Roubíček | | # permalink

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? ;)

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