Programovanie

Java Tip 68: Naučte sa, ako implementovať príkazový vzor v Jave

Dizajnové vzory nielen urýchľujú fázu návrhu objektovo-orientovaného projektu (OO), ale tiež zvyšujú produktivitu vývojového tímu a kvalitu softvéru. A Vzor príkazu je vzor správania objektu, ktorý nám umožňuje dosiahnuť úplné odpojenie medzi odosielateľom a príjemcom. (A. odosielateľ je objekt, ktorý vyvoláva operáciu, a a prijímač je objekt, ktorý dostane požiadavku na vykonanie určitej operácie. S oddelenie, odosielateľ nemá vedomosti o Prijímačrozhranie.) Pojem žiadosť tu sa odkazuje na príkaz, ktorý sa má vykonať. Vzor príkazu nám tiež umožňuje meniť, kedy a ako je požiadavka splnená. Preto nám príkazový vzor poskytuje flexibilitu, ako aj rozšíriteľnosť.

V programovacích jazykoch ako C, ukazovatele funkcií sa používajú na elimináciu gigantických vyhlásení o zmene. (Podrobnejší popis nájdete v časti „Java Tip 30: Polymorfizmus a Java“.) Pretože Java nemá funkčné ukazovatele, na implementáciu spätných volaní môžeme použiť príkazový vzor. Uvidíte to v akcii v prvom príklade kódu nazvanom nižšie TestCommand.java.

Vývojári zvyknutí používať ukazovatele funkcií v inom jazyku môžu byť v pokušení použiť server Metóda objekty Reflection API rovnakým spôsobom. Napríklad vo svojom článku „Java Reflection“ vám Paul Tremblett ukazuje, ako používať Reflection na implementáciu transakcií bez použitia príkazov switch. Odolal som tomuto pokušeniu, pretože Sun neodporúča používať Reflection API, keď budú stačiť iné nástroje, ktoré sú pre programovací jazyk Java prirodzenejšie. (Odkazy na článok o Tremblettovi a na stránku s výučbou Sun Reflection nájdete v Zdrojoch.) Ak program nepoužívate, bude sa dať ľahšie ladiť a udržiavať váš program. Metóda predmety. Namiesto toho by ste mali definovať rozhranie a implementovať ho do tried, ktoré vykonávajú potrebnú akciu.

Preto navrhujem, aby ste na implementáciu funkčných ukazovateľov použili príkazový vzor v kombinácii s mechanizmom dynamického načítania a väzby Javy. (Podrobnosti o mechanizme dynamického načítania a väzby Java nájdete v dokumente James Gosling a Henry McGilton nazvanom „The Java Language Environment - A White Paper“, uvedený v časti Zdroje.)

Pri sledovaní vyššie uvedeného návrhu využívame polymorfizmus poskytovaný aplikáciou príkazového vzoru na elimináciu príkazov obrovského prepínača, čo vedie k rozšíriteľným systémom. Taktiež využívame jedinečné mechanizmy dynamického načítania a väzby Javy na vytvorenie dynamického a dynamicky rozšíriteľného systému. Toto je ilustrované v druhom príklade kódu, ktorý sa volá nižšie TestTransactionCommand.java.

Vzor príkazu zmení samotnú požiadavku na objekt. Tento objekt je možné ukladať a obiehať okolo neho ako iné objekty. Kľúčom k tomuto vzoru je a Velenie rozhranie, ktoré deklaruje rozhranie pre vykonávanie operácií. V najjednoduchšej podobe obsahuje toto rozhranie abstrakt vykonať prevádzka. Každý betón Velenie trieda určuje dvojicu prijímač-akcia uložením Prijímač ako inštančná premenná. Poskytuje rôzne implementácie vykonať () metóda na vyvolanie požiadavky. The Prijímač má vedomosti potrebné na vybavenie žiadosti.

Obrázok 1 nižšie zobrazuje Prepnúť - agregácia Velenie predmety. Má otočiť() a flipDown () operácie v jeho rozhraní. Prepnúť sa nazýva vyvolávač pretože vyvolá operáciu vykonania v príkazovom rozhraní.

Konkrétny príkaz, LightOnCommand, realizuje vykonať činnosť príkazového rozhrania. Má vedomosti nazývať príslušné Prijímač činnosť objektu. V tomto prípade funguje ako adaptér. Podľa termínu adaptér, Myslím tým betón Velenie objekt je jednoduchý konektor, spájajúci Vyvolávač a Prijímač s rôznymi rozhraniami.

Klient vytvorí inštanciu Vyvolávač, Prijímača konkrétne objekty príkazov.

Obrázok 2, sekvenčný diagram, zobrazuje interakcie medzi objektmi. Ilustruje ako Velenie oddeľuje Vyvolávač z Prijímač (a žiadosť, ktorú vykonáva). Klient vytvorí konkrétny príkaz parametrizáciou svojho konštruktora pomocou vhodných parametrov Prijímač. Potom uloží Velenie v Vyvolávač. The Vyvolávač zavolá konkrétny príkaz, ktorý má vedomosti vykonať požadované Akcia () prevádzka.

Klient (hlavný program v zozname) vytvorí betón Velenie objekt a nastaví jeho Prijímač. Ako Vyvolávač objekt, Prepnúť ukladá betón Velenie objekt. The Vyvolávač vydá žiadosť volaním vykonať na Velenie objekt. Betón Velenie objekt vyvoláva operácie na svojom Prijímač vykonať žiadosť.

Kľúčovou myšlienkou je, že konkrétny príkaz sa zaregistruje do súboru Vyvolávač a Vyvolávač zavolá späť a vykoná príkaz na serveri Prijímač.

Príklad kódu príkazového vzoru

Pozrime sa na jednoduchý príklad ilustrujúci mechanizmus spätného volania dosiahnutý pomocou príkazového vzoru.

Príklad ukazuje a Ventilátor a a Svetlo. Naším cieľom je vyvinúť a Prepnúť ktoré môžu zapnúť alebo vypnúť akýkoľvek objekt. Vidíme, že Ventilátor a Svetlo majú rôzne rozhrania, čo znamená Prepnúť musí byť nezávislý od Prijímač rozhranie alebo nevie o kóde> Rozhranie prijímača. Na vyriešenie tohto problému je potrebné parametrizovať každý z parametrov Prepnúťs príslušným príkazom. Je zrejmé, že Prepnúť pripojený k Svetlo bude mať iný príkaz ako Prepnúť pripojený k Ventilátor. The Velenie trieda musí byť abstraktná alebo rozhranie, aby to fungovalo.

Keď konštruktér pre a Prepnúť je vyvolaná, je parametrizovaná príslušnou sadou príkazov. Príkazy budú uložené ako súkromné ​​premenné súboru Prepnúť.

Keď otočiť() a flipDown () operácie sa volajú, jednoducho vykonajú príslušný príkaz vykonať (). The Prepnúť nebude mať potuchy, čo sa stane v dôsledku vykonať () byť volaný.

TestCommand.java class Fan {public void startRotate () {System.out.println ("Ventilátor sa otáča"); } public void stopRotate () {System.out.println ("Ventilátor sa neotáča"); }} trieda Light {public void turnOn () {System.out.println ("Light is on"); } public void turnOff () {System.out.println ("Svetlo nesvieti"); }} trieda Switch {private Command UpCommand, DownCommand; verejný prepínač (príkaz hore, príkaz dole) {UpCommand = hore; // konkrétny príkaz sa zaregistruje u vyvolávača DownCommand = Down; } void flipUp () {// vyvolávač zavolá späť konkrétny Príkaz, ktorý vykoná Príkaz na prijímači UpCommand. vykonať (); } void flipDown () {DownCommand. vykonať (); }} trieda LightOnCommand implementuje príkaz {private Light myLight; public LightOnCommand (svetlo L) {myLight = L; } public void execute () {myLight. zapnúť( ); }} trieda LightOffCommand implementuje príkaz {private Light myLight; public LightOffCommand (svetlo L) {myLight = L; } public void execute () {myLight. vypnúť( ); }} trieda FanOnCommand implementuje príkaz {private Fan myFan; public FanOnCommand (Fan F) {myFan = F; } public void execute () {myFan. startRotate (); }} trieda FanOffCommand implementuje príkaz {private Fan myFan; public FanOffCommand (Fan F) {myFan = F; } public void execute () {myFan. stopRotate (); }} public class TestCommand {public static void main (String [] args) {Light testLight = new Light (); LightOnCommand testLOC = nový LightOnCommand (testLight); LightOffCommand testLFC = nový LightOffCommand (testLight); Prepínač testSwitch = nový prepínač (testLOC, testLFC); testSwitch.flipUp (); testSwitch.flipDown (); Test ventilátoraFan = nový ventilátor (); FanOnCommand foc = nový FanOnCommand (testFan); FanOffCommand ffc = nový FanOffCommand (testFan); Prepínač ts = nový prepínač (foc, ffc); ts.flipUp (); ts.flipDown (); }} Verejné rozhranie Command.java Príkaz {public abstract void execute (); } 

Vo vyššie uvedenom príklade kódu si všimnite, že vzor príkazu úplne oddelí objekt, ktorý vyvolá operáciu - (Prepínač) - od tých, ktorí majú vedomosti na jej vykonávanie - Svetlo a Ventilátor. To nám dáva veľkú flexibilitu: objekt vydávajúci žiadosť musí vedieť iba to, ako ju vydať; nemusí vedieť, ako sa žiadosť vykoná.

Vzor príkazu na implementáciu transakcií

Príkazový vzor je tiež známy ako akcia alebo transakčný vzor. Uvažujme o serveri, ktorý prijíma a spracováva transakcie doručované klientmi prostredníctvom pripojenia soketu TCP / IP. Tieto transakcie pozostávajú z príkazu, za ktorým nasleduje nula alebo viac argumentov.

Vývojári môžu použiť príkaz na prepnutie s veľkým a malým písmenom pre každý príkaz. Využitie Prepnúť vyhlásenia počas kódovania sú znakom zlého návrhu počas fázy návrhu objektovo orientovaného projektu. Príkazy predstavujú objektovo orientovaný spôsob podpory transakcií a je možné ich použiť na riešenie tohto problému s dizajnom.

V klientskom kóde programu TestTransactionCommand.java, všetky požiadavky sú zapuzdrené do všeobecných TransactionCommand objekt. The TransactionCommand konštruktor je vytvorený klientom a je registrovaný u CommandManager. Požiadavky vo fronte je možné vykonať v rôznom čase zavolaním na server runCommands (), čo nám dáva veľkú flexibilitu. Dáva nám tiež schopnosť zostaviť príkazy do zloženého príkazu. tiež mám CommandArgument, CommandReceivera CommandManager triedy a podtriedy TransactionCommand - menovite AddCommand a SubtractCommand. Nasleduje popis každej z týchto tried:

  • CommandArgument je pomocná trieda, ktorá uchováva argumenty príkazu. Môže byť prepísaný, aby sa zjednodušila úloha odovzdávania veľkého alebo variabilného počtu argumentov ľubovoľného typu.

  • CommandReceiver implementuje všetky metódy spracovania príkazov a je implementovaný ako Singletonov vzor.

  • CommandManager je vyvolávač a je Prepnúť ekvivalent z predchádzajúceho príkladu. Uchováva všeobecné TransactionCommand objekt v súkromí myCommand premenná. Kedy runCommands () je vyvolaná, volá sa vykonať () vhodných TransactionCommand objekt.

V prostredí Java je možné vyhľadať definíciu triedy s reťazcom obsahujúcim jej názov. V vykonať () prevádzka TransactionCommand triedy, vypočítam názov triedy a dynamicky ju prepojím so spusteným systémom - to znamená, že triedy sa načítajú za chodu podľa potreby. Ako názov podtriedy príkazu transakcie používam konvenciu pomenovania, názov príkazu zreťazený reťazcom „Command“, aby sa dal dynamicky načítať.

Všimnite si, že Trieda objekt vrátený newInstance () sa musí hodiť na vhodný typ. To znamená, že nová trieda musí buď implementovať rozhranie, alebo podtriedu existujúcej triedy, ktorá je programu známa v čase kompilácie. V tomto prípade, keďže implementujeme Velenie rozhranie, to nie je problém.

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