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
, CommandReceiver
a 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 jePrepnúť
ekvivalent z predchádzajúceho príkladu. Uchováva všeobecnéTransactionCommand
objekt v súkromímyCommand
premenná. KedyrunCommands ()
je vyvolaná, volá savykonať ()
vhodnýchTransactionCommand
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.