Príkazový vzor v Jave

1. Prehľad

Príkazový vzor je návrhový vzor správania a je súčasťou formálneho zoznamu návrhových vzorov GoF. Jednoducho povedané, vzor to chce zapuzdriť v objekte všetky údaje potrebné na vykonanie danej akcie (príkazu), vrátane metódy, ktorá sa má volať, argumentov metódy a objektu, ku ktorému metóda patrí.

Tento model nám to umožňuje oddeliť objekty, ktoré vytvárajú príkazy od svojich spotrebiteľov, preto je tento vzor bežne známy ako model výrobca-spotrebiteľ.

V tomto tutoriáli sa dozvieme, ako implementovať príkazový vzor v Jave pomocou objektovo-orientovaného aj objektovo-funkčného prístupu, a uvidíme, v akých prípadoch použitia to môže byť užitočné.

2. Objektovo orientovaná implementácia

V klasickej implementácii vyžaduje príkazový vzor implementácia štyroch komponentov: príkaz, príjemca, vyvolávateľ a klient.

Aby sme pochopili, ako vzor funguje a akú rolu hrá každá zložka, vytvorme si základný príklad.

Predpokladajme, že chceme vyvinúť aplikáciu textových súborov. V takom prípade by sme mali implementovať všetky funkcie potrebné na vykonávanie niektorých operácií súvisiacich so textovými súbormi, ako je otváranie, písanie, ukladanie textových súborov atď.

Aplikáciu by sme teda mali rozdeliť do štyroch vyššie spomenutých komponentov.

2.1. Veliteľské triedy

Príkaz je objekt, ktorého úlohou je uložiť všetky informácie potrebné na vykonanie akcievrátane metódy volania, argumentov metódy a objektu (známeho ako prijímač), ktorý metódu implementuje.

Ak chcete získať presnejšiu predstavu o tom, ako fungujú objekty príkazov, začnime vyvíjať jednoduchú vrstvu príkazov, ktorá obsahuje iba jedno jediné rozhranie a dve implementácie:

@FunctionalInterface verejné rozhranie TextFileOperation {String execute (); }
verejná trieda OpenTextFileOperation implementuje TextFileOperation {súkromný TextFile textFile; // konštruktory @Override public String execute () {return textFile.open (); }}
verejná trieda SaveTextFileOperation implementuje TextFileOperation {// rovnaké pole a konštruktor ako vyššie @Override public String execute () {return textFile.save (); }} 

V takom prípade TextFileOperation rozhranie definuje API príkazových objektov a dve implementácie, OpenTextFileOperation a SaveTextFileOperation, vykonať konkrétne činnosti. Prvým sa otvorí textový súbor, zatiaľ čo druhým sa uloží textový súbor.

Je jasne vidieť funkčnosť príkazového objektu: TextFileOperation príkazy zapuzdriť všetky požadované informácie na otvorenie a uloženie textového súboru vrátane objektu prijímača, metód volania a argumentov (v tomto prípade nie sú potrebné žiadne argumenty, ale mohli by byť).

Stojí za to to zdôrazniť komponent, ktorý vykonáva operácie so súbormi, je prijímač ( TextFile inštancia).

2.2. Trieda prijímača

Prijímač je objekt, ktorý vykonáva súbor súdržných akcií. Je to komponent, ktorý vykonáva skutočnú akciu, keď je príkaz vykonať () metóda sa volá.

V tomto prípade musíme definovať triedu prijímača, ktorej úlohou je modelovať TextFile objekty:

public class TextFile {private String name; // konštruktor public Reťazec open () {návrat "Otvárací súbor" + meno; } public String save () {návrat "Ukladanie súboru" + meno; } // ďalšie metódy textových súborov (úpravy, písanie, kopírovanie, vkladanie)} 

2.3. Trieda Invoker

Vyvolávač je objekt, ktorý vie, ako vykonať daný príkaz, ale nevie, ako bol príkaz implementovaný. Pozná iba rozhranie príkazu.

V niektorých prípadoch vyvolávač okrem ich vykonania tiež ukladá a zaraďuje príkazy do frontu. To je užitočné pri implementácii niektorých ďalších funkcií, ako je napríklad nahrávanie makier alebo funkcia späť a znovu.

V našom príklade je zrejmé, že musí existovať ďalší komponent zodpovedný za vyvolanie príkazových objektov a ich vykonávanie prostredníctvom príkazov ' vykonať () metóda. Presne tu vstupuje do hry trieda invokerov.

Pozrime sa na základnú implementáciu nášho vyvolávača:

public class TextFileOperationExecutor {private final List textFileOperations = new ArrayList (); public String executeOperation (TextFileOperation textFileOperation) {textFileOperations.add (textFileOperation); vrátiť textFileOperation.execute (); }}

The TextFileOperationExecutor trieda je len a tenká vrstva abstrakcie, ktorá oddeľuje objekty príkazov od ich spotrebiteľov a volá metódu zapuzdrenú v TextFileOperation príkazové objekty.

V tomto prípade trieda tiež uloží objekty príkazov do a Zoznam. To samozrejme nie je povinné pri implementácii vzorov, pokiaľ nepotrebujeme pridať ďalšiu kontrolu nad procesom vykonávania operácií.

2.4. Trieda klienta

Klient je objekt, ktorý riadi proces vykonania príkazu zadaním, ktoré príkazy sa majú vykonať a v ktorých fázach procesu sa majú vykonať.

Ak teda chceme byť ortodoxní s formálnou definíciou vzoru, musíme vytvoriť triedu klientov pomocou typických hlavný metóda:

public static void main (String [] args) {TextFileOperationExecutor textFileOperationExecutor = nový TextFileOperationExecutor (); textFileOperationExecutor.executeOperation (nový OpenTextFileOperation (nový TextFile ("file1.txt")))); textFileOperationExecutor.executeOperation (nový SaveTextFileOperation (nový TextFile ("file2.txt")))); } 

3. Objektovo-funkčná implementácia

Doteraz sme na implementáciu príkazového vzoru používali objektovo orientovaný prístup, ktorý je v poriadku a dobrý.

Od Javy 8 môžeme používať objektovo-funkčný prístup založený na výrazoch lambda a odkazoch na metódy urobte kód trochu kompaktnejším a menej podrobným.

3.1. Používanie výrazov Lambda

Ako TextFileOperation rozhranie je funkčné rozhranie, môžeme odovzdať vyvolávaciemu príkazu objekty vo forme výrazov lambda, bez toho aby ste museli vytvárať TextFileOperation inštancie výslovne:

TextFileOperationExecutor textFileOperationExecutor = nový TextFileOperationExecutor (); textFileOperationExecutor.executeOperation (() -> "Otváranie súboru file1.txt"); textFileOperationExecutor.executeOperation (() -> "Ukladanie súboru file1.txt"); 

Implementácia teraz vyzerá oveľa efektívnejšie a stručnejšie, ako sme to urobili znížil množstvo štandardného kódu.

Aj napriek tomu tu stále zostáva otázka: je tento prístup lepší v porovnaní s objektovo orientovaným?

No, to je zložité. Ak predpokladáme, že kompaktnejší kód znamená vo väčšine prípadov lepší kód, potom to tak je.

Ako základné pravidlo by sme mali hodnotiť na základe jednotlivých prípadov, kedy sa uchýliť k výrazom lambda.

3.2. Používanie odkazov na metódy

Podobne môžeme použiť odkazy na metódy pre odovzdávanie príkazových objektov vyvolávačovi:

TextFileOperationExecutor textFileOperationExecutor = nový TextFileOperationExecutor (); TextFile textFile = nový TextFile ("file1.txt"); textFileOperationExecutor.executeOperation (textFile :: open); textFileOperationExecutor.executeOperation (textFile :: save); 

V tomto prípade je implementácia trochu podrobnejšia ako tá, ktorá používa lambdy, pretože sme stále museli vytvárať TextFile inštancie.

4. Záver

V tomto článku sme sa naučili kľúčové koncepty príkazového vzoru a ako implementovať vzor v Jave pomocou objektovo orientovaného prístupu a kombinácie výrazov lambda a odkazov na metódy.

Ako obvykle sú všetky príklady kódu zobrazené v tomto tutoriále dostupné na GitHub.


$config[zx-auto] not found$config[zx-overlay] not found