IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 5 - Method chaining a method cascading

V minulé lekci, Immutable objects (neměnné objekty), jsme si probrali neměnné objekty, tzv. immutable objects.

V dnešním článku si vysvětlíme význam pomocných proměnných ve zdrojovém kódu a návrhový vzor Method chaining s fluent interface, který implementace tohoto vzoru poskytuje. Vysvětlíme si také Method cascading a v jakých případech nahrazuje Method chaining.

Motivace

Tvorba pomocných proměnných není vždy pro chod programu nezbytně nutná, ale je důležitá pro orientaci programátora ve zdrojovém kódu. Je běžnou praktikou vytvářet zbytečné proměnné, které program v podstatě zpomalují, ale umožňují člověku číst zdrojový kód. Někdy ovšem mohou být i na škodu a je užitečné se jich zbavit. Ukažme si příklady, kdy se pomocné proměnné vyplatí používat a kdy již ne.

Užitečné pomocné proměnné

Začněme kódem, který sčítá 2 čísla, která jsou zadána jako řetězce z konzole a následně jsou přeparsována na celočíselný typ int, sečtena a vypsána. Pomocných proměnných si nejprve vytvořme zbytečně mnoho:

string vstup1 = Console.ReadLine();
string vstup2 = Console.ReadLine();
int a = int.Parse(vstup1);
int b = int.Parse(vstup2);
int c = a + b;
Console.WriteLine(c);

Proměnné vstup1 a vstup2 jsou zde v podstatě zbytečné, můžeme se jich jednoduše zbavit, aniž by to zhoršilo čitelnost zdrojového kódu:

int a = int.Parse(Console.ReadLine());
int b = int.Parse(Console.ReadLine());
int c = a + b;
Console.WriteLine(c);

Metody jsme zanořili do sebe. Kód je nyní v ideální podobě, čte se jednoduše zleva doprava. Pokud bychom se zanořováním pokračovali, mohli bychom skončit až s jednořádkovým programem úplně bez pomocných proměnných:

Console.WriteLine(int.Parse(Console.ReadLine()) + int.Parse(Console.ReadLine()));

Kód výše se již ovšem čte poměrně špatně a určitě bychom jej neměli takto psát. Není to z toho důvodu, že je příliš mnoho metod na jednom řádku, ale že jsou metody rekurzivně zanořené a musíme je číst zprostředka a pamatovat si mezivýsledky.

Pozn.: Při problémech s výkonem se může na určitých místech aplikace někdy vyplatit začít se pomocných proměnných zbavovat. Takové situace ovšem řešíme vždy až nastanou, podle poučky Premature optimization is the root of all evil.

Zbytečné pomocné proměnné

Někdy jsou pomocné proměnné ale pouze na škodu. To je v případech, kdy potřebujeme nějak manipulovat stále s tím samým objektem a stále se odkazovat na proměnnou s ním, nebo když potřebujeme jeho instanci stále nějak upravovat. Uveďme si kód, který ze zadaného řetězce vyřízne prvních 50 znaků, odstraní okolo řetězce bílé znaky a převede jej celý na malá písmena. Jako opravdoví zelenáči bychom si vytvořili pro každý mezivýstup samostatnou proměnnou, to my dělat již nebudeme. Stále by nás ale mohlo napadnout výsledek přeukládat do pomocné proměnné:

string s = "Kdo je to Generál Failure a proč ten chlap čte zrovna z mého disku?";
s = s.Substring(0, 50);
s = s.Trim();
s = s.ToLower();

Stálé opakování `s = ` nemá z hlediska čitelnosti kódu žádný význam a jen zabírá místo. Jakékoli opakování ve zdrojovém kódu je téměř vždy návrhová chyba (viz poučka DRY).

Další zbytečné proměnné

Proměnné nám ve zdrojovém kódu překážejí také v případě, když nastavujeme různé vlastnosti na jednom objektu:

Tlacitko tlacitko = new Tlacitko();
tlacitko.Text = "Ulozit";
tlacitko.Sirka = 100;
tlacitko.Vyska = 50;
tlacitko.Ramecek = 2;
tlacitko.Barva = Barva.Modra;
tlacitko.Ikona = Ikona.Disketa;
tlacitko.Vybrane = true;
tlacitko.Zkratka = {"ctrl", "s"};

Kód je zbytečně nabobtnaný. Možná vás napadlo dát všechny parametry do konstruktoru, to by ovšem přehlednosti příliš nepomohlo, protože bychom zapomínali který je který. Např. hodnota 2 by působila jako velmi matoucí bez informace, že se jedná o rámeček a za předpokladu, že by bylo okolo tolik dalších hodnot.

Jak z toho ven?

Method chaining

Řetězení metod, někdy označované také jako fluent interface, je technika volání metody bezprostředně na návratové hodnotě jiné metody, bez použití pomocných proměnných. Tato technika je možná pouze za předpokladu, že objekt, na kterém metody voláme, s tímto používáním počítá, tedy, že vystavuje fluent interface. Method chaining je obvykle implementován tak, že metoda vrací buď přímo tu samou instanci objektu, na kterém je volána (tedy vrací this), nebo vrací objekt se stejným rozhraním. Často je použita na řetězcích, na setterech a na kolekcích. Nic samozřejmě nebrání jejímu využití na dalších typech objektů.

Určitě jste si někdy všimli, že na řetězcích můžeme volat metody jako ToLower(), Substring(), Trim() a podobně za sebou, jelikož vždy vrací řetězec a provádí se na řetězci. V tuto chvíli jste použili tzv. method chaining, čili zřetězení metod. Podívejme se, jak by vypadal onen odstrašující příklad s metodami na řetězci výše, kdybychom je provolali za sebou:

string s = "Kdo je to Generál Failure a proč ten chlap čte zrovna z mého disku?";
s = s.Substring(0, 50).Trim().ToLower();

Výsledek je velmi působivý a stále skvěle čitelný, jelikož metody voláme zleva doprava.

Kolekce

Method chaining se často využívá při práci s kolekcemi. Nemusí být již ani vraceny ty stejné datové typy, jen podporovaná rozhraní. V C# .NET bychom mohli z nějaké kolekce za použití Method chaining vybrat plnoleté uživatele, seřadit je podle věku, ty se stejným věkem ještě podle jména a vybrat jejich jména. Díky fluent interface to zvládneme na jediném řádku:

var vysledek = uzivatele.Where(u => u.Vek >= 18).OrderBy(u => u.Vek).ThenBy(u => u.Jmeno).Select(u => u.Jmeno);

V příkladu výše se využívá faktu, že každou metodu voláme na kolekci dat a jejím výsledkem je opět kolekce dat. Můžeme tedy volat další metodu přímo na návratové hodnotě předešlé metody. Výsledný zápis je velmi přehledný a připomíná dotazy v jazyce SQL. Pokud vás matou operátory šipek (=>), tak to je C# syntaxe pro lambda funkce, anonymní metody, pomocí kterých se specifikuje, co od jaké metody chceme.

Settery

A co příklad s instanciací tlačítka a nastavováním velkého počtu jeho parametrů? Do třídy implementujeme settery podporující fluent interface a parametry nastavíme pomocí nich:

Tlacitko tlacitko = new Tlacitko();
tlacitko.setText("Ulozit")
        .setSirka(100)
        .setVyska(50)
        .setRamecek(2)
        .setBarva(Barva.Modra)
        .setIkona(Ikona.Disketa)
        .setVybrane(true)
        .setZkratka({"ctrl", "s"});

Kód je nyní mnohem čitelnější a kratší. Dané metody uvnitř třídy Tlacitko by vypadaly asi takto:

public Tlacitko setText(string text)
{
    this.Text = text;
    return this;
}

public Tlacitko setSirka(int sirka)
{
    this.Sirka = sirka;
    return this;
}

public Tlacitko setVyska(int vyska)
{
    this.Vyska = vyska;
    return this;
}

public Tlacitko setRamecek(int ramecek)
{
    this.Ramecek = ramecek;
    return this;
}

// ...

Konstrukce objektu se spolu se settery často ještě vyčleňuje do samostatného objektu, tzv. Builderu, což zvyšuje čistotu kódu.

Method cascading

Některé programovací jazyky mají Method chaining zabudovaný přímo ve své syntaxi, např. jazyk Dart. O tomto mechanismu poté hovoříme jako o tzv. Method cascading. Obvykle je implementovaný přes operátor .. (dvě tečky) a umožňuje nám přistupovat k vlastnostem nebo volat metody na objektu, aniž bychom jej museli znovu a znovu specifikovat. Možná někteří z vám pamatují na konstrukci with ze stařičkého Pascalu, která dělala podobnou věc.

var tlacitko = new Tlacitko()
    ..text("Ulozit")
        ..sirka(100)
        ..vyska(50)
        ..ramecek(2)
        ..barva(Barva.Modra)
        ..ikona(Ikona.Disketa)
        ..vybrane(true)
        ..zkratka(["ctrl", "s"]);

Stejně můžeme volat i metody a Method chaining tedy plně nahradit, pokud to tedy náš programovací jazyk podporuje. Většina z nich to bohužel ještě neumí a proto se nejčastěji uchýlíme k implementaci fluent interface.


 

Předchozí článek
Immutable objects (neměnné objekty)
Všechny články v sekci
Ostatní návrhové vzory
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
12 hlasů
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity