IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

Factory (továrenské metóda)

Factory (alebo tiež po slovensky "faktorka") je jeden z najdôležitejších návrhových vzorov, ktorý umožňuje vyššiu abstrakciu pri vytváraní triedy než klasický konštruktor. Typicky sa používa pre zapuzdrenie zložitejšie inicializácia inštancie a pre vytváranie rôznych typov inštancií podľa reťazca.

Motivácia

V aplikáciách sa nám občas stáva, že potrebujeme vytvoriť inštanciu nejaké triedy a tú dodatočne inicializovať. Dobrým praktickým príkladom sú formulárové komponenty, u ktorých nestačí iba inštanciu vytvoriť, ale musíme ju tiež nastaviť veľa ďalších vlastností (rozmery, titulok, pozíciu, farbu ...). Ak niekde v aplikácii vytvárate 20 podobných tlačidiel a vytvorenie takéhoto tlačidla zaberá 10 riadkov, nutne vás napadne oddeliť tento kód do metódy. Gratulujem, práve ste vynašli factory. (Samozrejme má vzor nejaké ďalšie konvencie)

Faktorka môže tiež uchovávať premenné, ktoré potrebujeme k vytváraniu inštancií. Tieto premenné potom nemusí prestupovať celú aplikácií. Ďalšou výhodou je návratový typ, ktoré nemusia byť u faktorky špecifikovaný presne na typ objektu, ktorý vytvárame. Môžeme vracať niektorú z rodičovských tried alebo aj rozhranie. Na každý z týchto prípadov sa pozrieme bližšie.

Vzor sa nachádza v rôznych podobách a existujú aj jeho ďalšie variácie (factory, abstract factory, factory method, simple factory) a rôzne materiály je často vykladajú rôznym spôsobom. Základná myšlienka je však vždy rovnaká.

Factory Method

Návrhový vzor Factory method využíva metódy volajúci konštruktor. Má pomerne veľa rôznych podôb, niekedy môže byť použité dedenia a väčšinou sa píše proti rozhranie. My si tu ukážeme úplne najjednoduchšie implementáciu. Kľúčové je oddelenie konštrukcie konkrétnej inštancie do inej triedy, čím sa pôvodná trieda neznečistí konštrukčným kódom.

class Auto
{
    private string znacka;
    private string model;

    public Auto(string znacka, string model)
    {
        this.znacka = znacka;
        this.model = model;
    }

}

class TovarnaNaAuta
{
    public Auto VytvorFelicii()
    {
        return new Auto("Škoda", "Felicia");
    }
}

Máme tu jednoduchú triedu s verejným konstruktoru. Samozrejme môžeme tvoriť konkrétne inštancie automobilov:

Auto fabia = new Auto("Škoda", "Fabia");

Keďže v našej aplikácii často tvoríme Felicie alebo pre nás majú skrátka nejaký vyšší význam a zároveň nechceme zasahovať do triedy Auto, je ich konštrukcia zjednodušená na obyčajné zavolanie metódy továrenské triedy:

TovarnaNaAuta tovarna = new TovarnaNaAuta();
Auto felicia = tovarna.VytvorFelicii();

Keď si predstavíte, že Felicie má automaticky nastavených napr. 30 atribútov, tak sa vzor určite oplatí. A aj keby sme Feliciu potrebovali len na jednom mieste v programe, oddelení zložité inicializácia do inej triedy sprehľadní ďalšie logiku v triede, kde inštanciu potrebujeme.

Factory

Všeobecnejšom návrhom je Factory. V princípe sa jedná opäť o Factory Method, pretože to musí byť opäť metóda, ktorá našu inštanciu vytvára. Tu sú požiadavky omnoho voľnejšie. Metóda môže byť definovaná ako statická (niekedy aj v tej istej triede, s tým sa stretávame typicky u Javy). Ukážme si ďalší príklad:

class Auto
{
    private string znacka;
    private string model;

    private Auto(string znacka, string model)
    {
        this.model = model;
        this.znacka = znacka;
    }

    public static Auto Felicia()
    {
        return new Auto("Škoda", "Felicia");
    }
}

V tomto variante vzoru sa inštancie triedy nedá vytvoriť žiadnym iným spôsobom, než továrenské metódou (ale samozrejme môže byť konštruktor i verejný).

Inštanciu vytvoríme ako:

Auto felicia = Auto.Felicia();

Výhodou statické metódy priamo v triede je jednoduchšie implementácia. Samozrejme by sme ich nemali mať v triede moc a mali by byť nejako viazané na pôvodnú funkcionalitu triedy, inak by mali byť v samostatnej triede. Asi najsprávnejším príkladom takejto statickej metódy je získanie aktuálneho dátumu a času v C # .NET:

DateTime listopad = new DateTime(2018, 11, 24); // Konkrétní datum
DateTime dnes = DateTime.Now();

Metóda Now() vráti inštanciu DateTime, inicializuje na aktuálny dátum a čas. Takáto metóda priamo súvisí s funkcionalitou DateTime a preto je navrhovať správne, že je v tejto triede a aj statika tu dáva zmysel. Naopak, u našej Felicie() je to skôr odstrašujúci príklad, pretože s všeobecnou triedou Auto príliš nesúvisí.

Pozn .: VC # .NET je Now() vlastnosť, ktorá sa od metódy v nejakých detailoch odlišuje, v príklade bola uvedená ako metóda, aby zbytočne nezmátla programátorov v iných jazykoch.

Vytváranie inštancií rôznych tried

Návratová hodnota nemusí byť u Faktory rovnaká, ako je typ vytváranej inštancie. Pokojne môžeme vytvárať pri každom volaní inštanciu iné typ triedy a vracať iba rozhranie, ktoré všetky triedy implementujú. Ako príklad uvediem grafický program, ktorý vykresľuje rôzne obrazce na obrazovku. Povedzme teda, že máme štvorec, kruh a trojuholník, ktoré všetky implementujú rozhranie IVykreslitelny. Dáta získavame z reťazca (napr. Dekóduje nejaký textový súbor). Podľa dodaných dát sa rozhodneme, ktorý typ tvaru vytvoriť a vrátime ho iba ako rozhranie. Samotný program nevie, čo je to za obrazec. Iba vie, že ho možno vykresliť.

interface IVykreslitelny
{
    void Vykresli();
}

class Ctverec : IVykreslitelny { /* ... Kód ... */ }
class Trojuhelnik : IVykreslitelny { /* ... Kód ... */ }
class Kruh : IVykreslitelny { /* ... Kód ... */ }

class TvarFactory
{
    public IVykreslitelny Vytvor(string typ)
    {
        if (typ == "Ctverec")
            return new Ctverec();
        else if (typ == "Trojuhelnik")
            return new Trojuhelnik();
        else if (typ == "Kruh")
            return new Kruh();
    }
}

// použití v programu
Factory faktorka = new TvarFactory();
IVykreslitelny instance = faktorka.Vytvor("Ctverec");

Vidíme, že typ sa mení. Jediné, čo program vie, je, že možno objekt vykresliť, ale už nič o tom ako sa vykreslí. To by ho však nemalo ani zaujímať, pretože by sa o vykreslenie starať nemal. Buď sa o vykreslenie stará opäť konkrétna trieda alebo samotnej objekty.

Ak by tvarov bolo viac typov, je výhodnejšie použiť reflexiu a dynamicky vytvárať inštancie tried, ktorých názov korešponduje s názvom tvaru. Nesmieme tu zabudnúť nejako ošetriť, aby bolo vytváranie tried obmedzené na určitý balík, inak by si užívateľ mohol vytvoriť prakticky čokoľvek z našej aplikácie.

Návrhový vzor Factory - GOF - Vzory pre vytváranie

Závislosť na parametroch

Niekedy potrebujeme vytvoriť inštanciu triedy, ale parametre pre konštruktor získame na inom mieste v programe. Konštruktor využiť nemôžeme, pretože ešte nepoznáme všetky parametre a vytvoriť neinicializované inštanciu napr. Bezparametrickým konstruktoru nie je dobrá praktika. Ak využijeme Factory triedu, môžeme jej dáta postupne odovzdávať a ona si ich bude uchovávať. Vo chvíli, keď dát bude dostatok, môžeme inštanciu vytvoriť. Nemusíme sa teda starať o žiadne odovzdávanie dát medzi časťami programu, odovzdáme iba inštanciu triedy Factory.

Do tohto článku bude postupne doplňovaná ďalšie implementácie rôznych typov továrniček. Chcete nám pomocou svojou skúsenosťou s factory? Napíšte nám do komentárov.


 

Všetky články v sekcii
GOF - Vzory pre vytváranie
Článok pre vás napísal Patrik Valkovič
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu (NodeJS).
Aktivity