Přidej si svou IT školu do profilu a najdi spolužáky zde na síti :)

Method chaining a method cascading

Návrh Návrhové vzory Method chaining a method cascading

ONEbit hosting Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

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 = 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ůžete 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 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(s).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 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.


 

 

Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
5 hlasů
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Miniatura
Všechny články v sekci
Návrhové vzory
Aktivity (2)

 

 

Komentáře

Avatar
Honza Bittner
Šupák
Avatar
Honza Bittner:20. října 23:30

Dart. (v)

Odpovědět  +1 20. října 23:30
Milovník Dartu. Student FIT ČVUT. Sleduj mě na https://twitter.com/tenhobi a ptej se na cokoli na https://github.com/...
Avatar
Václav Houra:30. října 20:12

Místo s.Trim(s) by mělo stačit s.Trim(). Nenapadá mne žádný důvod, proč by objekt ve své metodě měl dostat sama sebe jako parametr. Ale jinak zajímavé. Díky

 
Odpovědět 30. října 20:12
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 2 zpráv z 2.