Parancs (tervezési minta)

Az oldal jelenlegi verzióját még nem ellenőrizték tapasztalt közreműködők, és jelentősen eltérhet a 2019. szeptember 20-án felülvizsgált verziótól ; az ellenőrzések 8 szerkesztést igényelnek .
Csapat
parancs
Típusú viselkedési
Célja hogy a parancsot objektumként dolgozza fel
Kapcsolódó sablonok Linker , Keeper , Prototype , Loner
Leírása: Tervezési minták Igen

A parancs egy viselkedési  tervezési minta , amelyet az objektum - orientált programozásban használnak, és amely egy műveletet reprezentál . A parancsobjektum magát a műveletet és annak paramétereit tartalmazza.

Cél

Hozzon létre egy olyan struktúrát, ahol a küldő osztály és a fogadó osztály nem függ közvetlenül egymástól. Visszahívás szervezése egy olyan osztályba, amely tartalmazza a feladó osztályt.

Leírás

Az objektum-orientált programozásban a Command tervezési minta egy viselkedési minta, amelyben egy objektumot használnak a művelet végrehajtásához vagy egy esemény későbbi előidézéséhez szükséges összes információ beágyazására. Ez az információ tartalmazza a metódus nevét, a metódust birtokló objektumot és a metódus paramétereinek értékeit.

A parancsmintához mindig négy kifejezés kapcsolódik: parancsok (parancs), parancsfogadó (vevő), parancshívó (invoker) és kliens (kliens). A Command objektum tud a vevőről, és meghívja a vevő metódusát. A vevő paraméterei a parancsban tárolódnak. A hívó (invoker) tudja, hogyan kell végrehajtani a parancsot, és esetleg nyomon követi a végrehajtott parancsokat. A hívó (invoker) semmit sem tud egy adott parancsról, csak az interfészről tud. Mindkét objektum (a hívó objektum és több parancsobjektum) a kliens objektumhoz tartozik. A kliens dönti el, hogy mely parancsokat és mikor hajtsa végre. A parancs végrehajtásához átadja a parancs objektumot a hívónak (invoker).

A parancsobjektumok használata megkönnyíti a megosztott összetevők felépítését, amelyeket bármikor delegálhat vagy metódushívásokat kell végrehajtania anélkül, hogy ismernie kellene az osztálymetódusokat vagy a metódusparamétereket. A hívó objektum (invoker) használata lehetővé teszi a végrehajtott parancsok nyilvántartását anélkül, hogy az ügyfélnek tudnia kellene erről az elszámolási modellről (az ilyen elszámolás hasznos lehet például a visszavonás és újraindítás parancs végrehajtásához).

Alkalmazás

A parancsminta a következő esetekben lehet hasznos.

A felhasználói felület gombjai és menüpontjai

A Swingben és a Borland Delphiben az Action egy parancsobjektum. A kívánt parancs végrehajtása mellett a művelethez tartozhat ikon, billentyűparancs, eszköztipp szövege stb. Az eszköztár gombja vagy menüeleme teljes mértékben inicializálható csak egy Action objektum használatával .

Makró felvétel

Ha az összes felhasználói művelet parancsobjektumként van ábrázolva, a program rögzítheti a műveletek sorozatát úgy, hogy egyszerűen tárolja a parancsobjektumok listáját a végrehajtás sorrendjében. Ezután ugyanazokat a műveleteket „ismételheti”, ha ugyanazokat a parancsobjektumokat ugyanabban a sorrendben hajtja végre.

Többszintű visszavonási műveletek ( Visszavonás )

Ha a programban minden felhasználói művelet parancsobjektumként van megvalósítva, a program el tudja menteni az utoljára végrehajtott parancsok veremét. Amikor a felhasználó törölni akar egy parancsot, a program egyszerűen kiugrik az utolsó parancsobjektumot, és végrehajtja az undo() metódust .

hálózatok

Parancsobjektumokat küldhet a hálózaton keresztül egy másik gépen végrehajtandó, például egy számítógépes játék játékos műveletéhez.

Haladásjelző sávok

Tegyük fel, hogy egy programnak van egy parancssorozata, amelyet sorrendben hajt végre. Ha minden parancsobjektum rendelkezik getEstimatedDuration() metódussal , a program könnyen meg tudja becsülni a folyamat teljes időtartamát. Megjeleníthet egy folyamatjelző sávot, amely megmutatja, milyen közel áll a program az összes feladat elvégzéséhez.

Menet medencék

Egy tipikus általános célú szálkészletosztály rendelkezhet egy addTask() metódussal , amely egy munkaelemet ad hozzá a függőben lévő feladatok belső sorához. Olyan szálkészletet tart fenn, amely parancsokat hajt végre a sorból. A sorban lévő elemek parancsobjektumok. Ezek az objektumok általában olyan közös felületet valósítanak meg, mint például a java.lang.Runnable , amely lehetővé teszi a szálkészlet számára, hogy parancsokat futtasson még akkor is, ha a szálkészlet anélkül íródott, hogy ismeretei volna a konkrét feladatokról, amelyekhez használni fogják.

Tranzakciók

Az "visszavonás" művelethez hasonlóan az adatbázis-kezelő rendszer (DBMS) vagy a szoftvertelepítő tárolhatja a végrehajtott vagy végrehajtandó műveletek listáját. Ha az egyik meghibásodik, akkor az összes többi törölhető vagy eldobható (általánosan visszaállításnak nevezik). Például, ha két kapcsolódó adatbázistáblát kell frissíteni, és a második frissítés sikertelen, a rendszer visszaállíthatja a tranzakciót, hogy az első tábla ne tartalmazzon érvénytelen hivatkozást.

Mesterek

Gyakran egy varázsló (telepítő varázsló vagy bármi más) több konfigurációs oldalt jelenít meg egyetlen művelethez, amely csak akkor történik meg, ha a felhasználó az utolsó oldalon lévő „Befejezés” gombra kattint. Ezekben az esetekben a felhasználói felület kódjának az alkalmazáskódtól való elkülönítésének természetes módja a varázsló végrehajtása egy parancsobjektummal. A parancsobjektum a varázsló első megjelenésekor jön létre. A varázsló minden oldala elmenti a parancsobjektum módosításait, így az objektum feltöltődik a felhasználó navigálása közben. A "Kész" gomb egyszerűen elindítja az execute() metódus végrehajtását.

Példák

C++ példa

Forrásszöveg C++ nyelven # include < iostream > # include < vektor > # include < string > namespace std használatával ; class Dokumentum { vektor < string > adat ; public : Document ( ) { adatok . tartalék ( 100 ); // legalább 100 sorig } void Insert ( int line , const string & str ) { if ( line <= data . size ( ) ) data . beszúr ( adat . kezd () + sor , str ); else cout << "Hiba!" << endl ; } void Remove ( int line ) { if ( !( line > data . size ( ) ) ) adatok . törlés ( data.begin ( ) + sor ) ; else cout << "Hiba!" << endl ; } string & operátor [] ( int x ) { return data [ x ]; } void Mutasd () { for ( int i = 0 ; i < adat . méret (); ++ i ) { cout << i + 1 << ". " << adat [ i ] << endl ; } } }; class Parancs { védett : Dokumentum * doc ; public : virtual ~ Command () {} virtual void Végrehajtás () = 0 ; virtual void unExecute () = 0 ; void setDocument ( Dokumentum * _doc ) { doc = _doc ; } }; class InsertCommand : public Command { int line ; string str ; public : InsertCommand ( int _line , const string & _str ): line ( _line ), str ( _str ) {} void Végrehajtás () { doc -> Insert ( sor , str ); } void unExecute () { doc -> Remove ( sor ); } }; class Invoker { vector < Command *> DoneCommands ; Dokumentum doc ; Parancs * parancs ; public : void Insert ( int sor , string str ) { parancs = new InsertCommand ( sor , str ); parancs -> setDocument ( & doc ); parancs -> Végrehajtás (); DoneCommands . push_back ( parancs ); } void Undo () { if ( DoneCommands . size () == 0 ) { cout << "Nincs mit visszavonni!" << endl ; } else { command = DoneCommands . vissza (); DoneCommands . pop_back (); parancs -> unexecute (); // Ne felejtsd el törölni a parancsot!!! törlés parancs ; } } void Mutasd () { doc . show (); } }; int main () { char s = '1' ; int sor , sor_b ; string str ; Invoker inv ; while ( s != 'e' ) { cout << "Mi a teendő: \n1.Sor hozzáadása\n2.Utolsó parancs visszavonása" << endl ; cin >> s ; switch ( s ) { case '1' : cout << "Milyen sort kell beszúrni: " ; cin >> sor ; --sor ; _ cout << "Mit kell beszúrni: " ; cin >> str ; inv . beszúrás ( sor , str ); szünet ; eset '2' : bev . Visszavonás (); szünet ; } cout << "$$$DOKUMENTUM$$$" << endl ; inv . show (); cout << "$$$DOCUMENT$$$" << endl ; } }

Python példa

Forráskód Pythonban abc importból ABCMeta , abstractmethod _ osztály Csapat : """ Vevő - Csapatobjektum """ def move ( self , direction : str ) -> None : """ Mozgás indítása egy bizonyos irányba """ print ( 'Az osztag mozgásba kezdett {} ' . format ( irány )) def stop ( self ) -> None : """ Stop """ print ( 'Squad stop' ) class Parancs ( metaclass = ABCMeta ): """ Alaposztály minden parancshoz """ @abstractmethod def execute ( self ) -> None : """ Folytassa a """ parancs végrehajtásával. @abstractmethod def unexecute ( self ) -> None : """ A "" " parancs végrehajtásának visszavonása osztály AttackCommand ( Command ): """ A támadást végrehajtó parancs : """ def __init__ ( self , troop : Troop ) -> None : """ Konstruktor. :param troop: az a csapat, amelyhez a " "" parancs a self -hez kapcsolódik .troop = csapat def execute ( self ) -> None : self . csapat . mozgás ( 'előre' ) def unexecute ( self ) -> None : self . csapat . megáll () osztály RetreatCommand ( Command ): """ Retreat parancs """ def __init__ ( self , troop : Troop ) -> None : """ Konstruktor. :param troop: az a csapat, amelyhez a """ self parancs társítva van . csapat = csapat def execute ( self ) -> None : self . csapat . mozog ( 'vissza' ) def unexecute ( self ) -> None : self . csapat . megáll () osztály TroopInterface : """ Invoker - egy felület, amelyen keresztül parancsokat adhat ki egy adott osztagnak """ def __init__ ( self , attack : AttackCommand , retreat : RetreatCommand ) -> None : """ Constructor. :param attack: attack command :param retreat: visszavonulási parancs " "" self .attack_command = self attack .retreat_command = visszavonulás self .current_command = Nincs # jelenleg futó parancs def attack ( self ) -> None : self . aktuális_parancs = self . attack_command self . attack_command . végrehajtani () def retreat ( self ) -> None : self . aktuális_parancs = self . retreat_command self . retreat_command . végrehajtani () def stop ( self ) -> None : if self . current_command : self . aktuális_parancs . unexecute () self . current_command = Nincs más : print ( 'Az egység nem tud leállni, mert nem mozog' ) if __name__ == '__main__' : troop = Csapat () interface = TroopInterface ( AttackCommand ( csapat ), RetreatCommand ( csapat )) interfész . támadás () interfész . stop () interfész . visszavonulás () interfész . megáll ()

PHP5 példa

PHP5 forráskód <?php /** * Abstract class "parancsok" * @abstract */ abstract class Parancs { public abstract function Végrehajtás (); public absztrakt függvény UnExecute (); } /** * A konkrét "parancs" osztálya */ class CalculatorCommand extends Command { /** * Aktuális parancsművelet * * @var string */ public $operator ; /** * Aktuális operandus * * @var vegyes */ public $ operand ; /** * A parancs osztálya * * @var osztály Calculator objektumához */ public $calculator ; /** * Konstruktor * * @param objektum $kalkulátor * @param string $operátor * @param vegyes $ operand */ public function __construct ( $számológép , $operátor , $operandus ) { $this -> calculator = $calculator ; $this -> operátor = $operátor ; $this -> operandus = $operandus ; } /** * Újraimplementált szülő::Execute() function */ public function Execute () { $this -> calculator -> Operation ( $this -> operator , $this -> operandus ); } /** * Újraimplementált szülő::UnExecute() function */ public function UnExecute () { $this -> calculator -> Operation ( $this -> Undo ( $this -> operator ), $this -> operandus ); } /** * Milyen műveletet kell visszavonni? * * @private * @param string $operator * @return string */ private function Undo ( $operator ) { //minden végrehajtott művelet fordítottja keresése switch ( $operator ) { case '+' : $undo = '-' ; szünet ; eset '-' : $undo = '+' ; szünet ; eset '*' : $undo = '/' ; szünet ; eset '/' : $undo = '*' ; szünet ; alapértelmezett : $undo = ' ' ; szünet ; } return $visszavonás ; } } /** * A "parancsok" osztályfogadója és végrehajtója */ class Calculator { /** * A parancsvégrehajtás aktuális eredménye * * @private * @var int */ private $ curr = 0 ; public function Művelet ( $operátor , $operandus ) { //operátor kiválasztása az eredmény kiszámításához switch ( $operátor ) { case '+' : $this -> curr += $operand ; szünet ; case '-' : $this -> curr -= $operand ; szünet ; case '*' : $this -> curr *= $operand ; szünet ; case '/' : $this -> curr /= $operand ; szünet ; } print ( "Aktuális eredmény = $this->curr (a $operator c $operand végrehajtása után )" ); } } /** * Parancsokat hívó osztály */ class User { /** * Ez az osztály a végrehajtandó parancsokat fogja kapni * * @private * @var osztály objektuma Számológép */ private $calculator ; /** * Műveletek tömbje * * @private * @var array */ private $commands = array (); /** * Aktuális parancs a műveleti tömbben * * @private * @var int */ private $current = 0 ; public function __construct () { // létrehozunk egy példányt az osztályból, amely parancsokat hajt végre $this -> calculator = new Számológép (); } /** * A visszavont parancsok visszaadására szolgáló függvény * * @param int $levels visszaküldendő műveletek száma */ public function Újra ( $levels ) { print ( " \n ---- $levels műveletek ismétlése " ); // Visszatérési műveletek for ( $i = 0 ; $i < $levels ; $i ++ ) if ( $this -> current < count ( $this -> commands ) - 1 ) $this -> parancsok [ $this - > aktuális ++ ] -> Végrehajtás (); } /** * Undo function parancs * * @param int $levels visszavonási műveletek száma */ public function Undo ( $levels ) { print ( " \n ---- $levels műveletek visszavonása " ); // Műveletek visszavonása a következőhöz: ( $i = 0 ; $i < $levels ; $i ++ ) if ( $this -> current > 0 ) $this -> parancsok [ -- $this -> current ] -> UnExecute ( ); } /** * Parancsvégrehajtási függvény * * @param string $operátor * @param vegyes $operand */ public function Compute ( $operátor , $operand ) { // Műveleti parancs létrehozása és végrehajtása $command = new CalculatorCommand ( $this - > számológép , $operátor , $operandus ); $parancs -> Végrehajtás (); // Művelet hozzáadása a műveleti tömbhöz és az aktuális művelet számlálójának növelése $this -> commands [] = $command ; $this -> aktuális ++ ; } } $user = új felhasználó (); // Tetszőleges parancsok $user -> Compute ( '+' , 100 ); $user -> Compute ( '-' , 50 ); $user -> Compute ( '*' , 10 ); $user -> Compute ( '/' , 2 ); // Visszavonás 4 parancs $user -> Undo ( 4 ); // 3 visszavont parancs visszaadása. $user -> Redo ( 3 );

Java példa

Java forrás

Annak érdekében, hogy a műveletek nevei megfeleljenek a műveletnek, a lámpán végzett műveletek (bekapcsolás, kikapcsolás) átkerülnek az osztályok egy példányába, SwitchOnCommandés SwitchOffCommandmindkét osztály megvalósítja az interfészt Command.

import java.util.HashMap ; /** A parancsfelület */ interface Command { void execute (); } /** Az Invoker osztály */ class Switch { private final HashMap < String , Command > commandMap = new HashMap <> (); public void register ( String commandName , Command parancs ) { commandMap . put ( parancsNév , parancs ); } public void execute ( String commandName ) { Command command = commandMap . get ( parancsnév ); if ( parancs == null ) { throw new IllegalStateException ( "nincs parancs regisztrálva a következőhöz: " + parancsnév ); } parancs . végrehajtani (); } } /** A Receiver class */ class Light { public void turnOn () { System . ki . println ( "A lámpa világít" ); } public void turnOff () { Rendszer . ki . println ( "A lámpa nem világít" ); } } /** A lámpa bekapcsolásának parancsa - ConcreteCommand #1 */ class SwitchOnCommand implements Command { private final Light light ; public SwitchOnCommand ( Fénylámpa ) { this . _ fény = fény ; } @Override // Parancs public void execute () { light . bekapcsolás (); } } /** A lámpa kikapcsolásának parancsa - ConcreteCommand #2 */ class SwitchOffCommand implements Command { private final Light light ; public SwitchOffCommand ( Fénylámpa ) { this . _ fény = fény ; } @Override // Parancs public void execute () { light . kikapcsolás (); } } public class CommandDemo { public static void main ( final String [] argumentumok ) { Light lamp = new Light (); Command switchOn = új SwitchOnCommand ( lámpa ); Parancs kikapcsolás = new SwitchOffCommand ( lámpa ); Switch mySwitch = new Switch (); mySwitch . regisztrál ( "on" , switchOn ); mySwitch . register ( "ki" , kikapcsolás ); mySwitch . végrehajt ( "on" ); mySwitch . végrehajt ( "off" ); } } A funkcionális interfész használata

A Java 8-tól kezdődően nem kötelező osztályokat létrehozni SwitchOnCommand, SwitchOffCommandhelyette használhatunk operátort ::, ahogy az a következő példában látható

public class CommandDemo { public static void main ( final String [] argumentumok ) { Light lamp = new Light (); Parancskapcsoló On = lámpa :: be ; Kikapcsolási parancs = lámpa :: kikapcsol ; Switch mySwitch = new Switch (); mySwitch . regisztrál ( "on" , switchOn ); mySwitch . register ( "ki" , kikapcsolás ); mySwitch . végrehajt ( "on" ); mySwitch . végrehajt ( "off" ); } }

Swift 5 példa

Forráskód a Swift 5-ben protokoll Parancs { func execute() } // hívó osztály kapcsoló { enum SwitchAction { tok be, ki } var állapot: String? var akció: Fény? func register(_ parancs: Light) { self.action = parancs } func execute(_ commandName: SwitchAction) { if commandName == .on { akció?.turnOn() } else if commandName == .off { akció?.turnOff() } } } // Vevő osztály Fény { func turnOn() { print ("A lámpa világít") } func turnOff() { print("A lámpa nem világít") } } class SwitchOnCommand: Command { privát var light: Light init(_light: Light) { én.light = fény } func execute() { light.turnOn() } } class SwitchOffCommand: Command { privát var light: Light init(_light: Light) { én.light = fény } func execute() { light.turnOff() } } // HASZNÁLAT let invoker = Switch() hagyd a vevőt = Light() invoker.register(receiver) invoker.execute(.on)

Ruby példa

Ruby forráskód modul EngineCommands # Absztrakt osztály 'Command' osztály Parancs def execute end end # Vevő osztály Motor attr_reader :state def inicializálja az rpm @state , @rpm = false , rpm if rpm . egy? Egész szám vége def turnOn ; @állapot = igaz ; end def turnOff ; @állapot = false ; vége vége # ConcreteCommand1 class CommandTurnOn < Command def inicialize engine @engine = engine if engine . egy? motor vége def execute @engine . turnOn end end # ConcreteCommand2 osztály CommandTurnOff < Command def inicialize engine @engine = engine if engine . egy? motor vége def execute @engine . kapcsolja ki a végét # Invoker class Invoker def inicializál @commands = Hash . új vég def registerCommand parancsnév , parancs @commands [ commandName ] = parancs if parancs . egy? Command és @commands [ commandName ]. egy? NilClass vége def executeCommand commandName @command = @commands [ commandName ] hacsak nem @command . egy? NilClass @command . execute else raise TypeError . new end end end end # Kliens modul Az ügyfél tartalmazza az EngineCommands parancsot invoker = Invoker . új motor = motor . új ( 250 ) commandTurnOn = CommandTurnOn . new ( engine ) commandTurnOff = CommandTurnOff . új ( motor ) hívó . registerCommand "turnOn" , commandTurnOn invoker . registerCommand "turnOff" , commandTurnOff " \t Motorállapot a parancs használata előtt: #{ engine . state } " # => Motorállapot a parancs használata előtt: false a " \t Motor állapota a ' turnOn ' parancs használata után: #{ invoker . executeCommand "turnOn" } " # => Motorállapot a 'turnOn' parancs használata után: true: " \ t Motor állapota használat után parancs 'turnOff': #{ invoker . executeCommand "turnOff" } " # => Motor állapota használat után parancs 'turnOff': false end

Linkek