Komento (suunnittelukuvio)

Kokeneet kirjoittajat eivät ole vielä tarkistaneet sivun nykyistä versiota, ja se voi poiketa merkittävästi 20. syyskuuta 2019 tarkistetusta versiosta . tarkastukset vaativat 8 muokkausta .
Tiimi
komento
Tyyppi käyttäytymiseen
Tarkoitus käsitellä komennon objektina
Aiheeseen liittyvät mallit Linkkeri , Keeper , Prototype , Loner
Kuvattu suunnittelukuvioissa Joo

Komento on olio - ohjelmoinnissa käytetty käyttäytymissuunnittelumalli  , joka edustaa toimintaa. Komentoobjekti sisältää itse toiminnon ja sen parametrit.

Tarkoitus

Luo rakenne, jossa lähettäjäluokka ja vastaanottajaluokka eivät ole suoraan riippuvaisia ​​toisistaan. Takaisinsoiton järjestäminen luokkaan, joka sisältää lähettäjän luokan.

Kuvaus

Olio-ohjelmoinnissa Command-suunnittelumalli on käyttäytymismalli, jossa objektia käytetään kapseloimaan kaikki tiedot, joita tarvitaan toiminnon suorittamiseen tai tapahtuman herättämiseen myöhemmin. Nämä tiedot sisältävät menetelmän nimen, menetelmän omistavan objektin ja menetelmän parametrien arvot.

Komentomalliin liittyy aina neljä termiä: komennot (komento), komennon vastaanottaja (vastaanotin), komennon kutsuja (kutsuja) ja asiakas (asiakas). Komento-olio tietää vastaanottimesta ja kutsuu vastaanottimen menetelmän. Vastaanottimen parametriarvot tallennetaan komentoon. Soittaja (kutsuja) osaa suorittaa komennon ja mahdollisesti pitää kirjaa suoritetuista komennoista. Kutsuja (kutsuja) ei tiedä tietystä komennosta mitään, se tietää vain käyttöliittymän. Molemmat objektit (kutsuttava objekti ja useat komentoobjektit) kuuluvat asiakasobjektiin. Asiakas päättää, mitkä komennot suorittaa ja milloin. Suorittaakseen komennon se välittää komentoobjektin kutsujalle (kutsujalle).

Komentoobjektien avulla on helppo rakentaa jaettuja komponentteja, jotka sinun on delegoitava tai tehtävä menetelmäkutsuja milloin tahansa ilman, että sinun tarvitsee tietää luokan menetelmiä tai menetelmäparametreja. Soittajaobjektin (invoker) avulla voit pitää kirjaa suoritetuista komennoista ilman, että asiakkaan tarvitsee tietää tästä laskentamallista (sellainen kirjanpito voi olla hyödyllinen esimerkiksi kumoa- ja uudelleen-komentojen toteuttamisessa).

Sovellus

Komentokavio voi olla hyödyllinen seuraavissa tapauksissa.

Käyttöliittymän painikkeet ja valikon kohdat

Swingissä ja Borland Delphissä toiminto on komentoobjekti. Sen lisäksi, että toiminto voi suorittaa halutun komennon, siihen voi liittyä kuvake, pikanäppäin, työkaluvihjeteksti ja niin edelleen. Työkalupalkin painike tai valikkokohta voidaan alustaa kokonaan käyttämällä vain Action - objektia .

Makrotallennus

Jos kaikki käyttäjän toiminnot esitetään komentoobjekteina, ohjelma voi tallentaa toimintosarjan yksinkertaisesti tallentamalla luettelon komentoobjekteista siinä järjestyksessä, jossa ne suoritetaan. Se voi sitten "toistaa" samat toiminnot suorittamalla samat komentoobjektit samassa järjestyksessä.

Monitasoiset kumoamistoiminnot ( Kumoa )

Jos kaikki käyttäjän toiminnot ohjelmassa on toteutettu komentoobjekteina, ohjelma voi tallentaa pinon viimeksi suoritetuista komennoista. Kun käyttäjä haluaa peruuttaa komennon, ohjelma yksinkertaisesti ponnahtaa esiin viimeisen komentoobjektin ja suorittaa sen undo()- menetelmän .

verkkoja

Voit lähettää komentoobjekteja verkon yli suoritettaviksi toisella koneella, kuten pelaajan toiminto tietokonepelissä.

Edistymispalkit

Oletetaan, että ohjelmalla on sarja komentoja, jotka se suorittaa järjestyksessä. Jos jokaisessa komentoobjektissa on getEstimatedDuration() -metodi , ohjelma voi helposti arvioida prosessin kokonaiskeston. Se voi näyttää edistymispalkin, joka kertoo, kuinka lähellä ohjelma on kaikkien tehtävien suorittamiseen.

Lanka-altaat

Tyypillisessä yleiskäyttöisessä säievarastoluokassa voi olla addTask() -metodi , joka lisää työkohteen odottavien tehtävien sisäiseen jonoon. Se ylläpitää joukkoa säikeitä, jotka suorittavat komentoja jonosta. Jonon elementit ovat komentoobjekteja. Tyypillisesti nämä objektit toteuttavat yhteisen käyttöliittymän, kuten java.lang.Runnable , jonka avulla säiepooli voi suorittaa komentoja, vaikka se olisi kirjoitettu tietämättä mitään erityisistä tehtävistä, joihin sitä käytetään.

Tapahtumat

Kuten "kumoa" -toiminto, tietokannan hallintajärjestelmä (DBMS) tai ohjelmiston asennusohjelma voi tallentaa luettelon toiminnoista, jotka on suoritettu tai tullaan suorittamaan. Jos yksi niistä epäonnistuu, kaikki muut voidaan peruuttaa tai hylätä (kutsutaan yleisesti palautukseksi). Jos esimerkiksi kaksi toisiinsa liittyvää tietokantataulukkoa on päivitettävä ja toinen päivitys epäonnistuu, järjestelmä voi peruuttaa tapahtuman, jotta ensimmäinen taulukko ei sisällä virheellistä linkkiä.

Mestarit

Usein ohjattu toiminto (ohjattu asennustoiminto tai mikä tahansa) esittää useita määrityssivuja yhdelle toiminnolle, joka tapahtuu vain, kun käyttäjä napsauttaa viimeisellä sivulla olevaa "Valmis"-painiketta. Näissä tapauksissa luonnollinen tapa erottaa käyttöliittymäkoodi sovelluskoodista on toteuttaa ohjattu toiminto komentoobjektilla. Komentoobjekti luodaan, kun ohjattu toiminto näytetään ensimmäisen kerran. Kukin ohjatun toiminnon sivu tallentaa muutokset komentoobjektiin, joten objekti täytetään käyttäjän navigoidessa. "Valmis"-painike yksinkertaisesti käynnistää execute() -menetelmän suorittamisen.

Esimerkkejä

C++ esimerkki

Lähdeteksti C++:ssa # include < iostream > # include < vector > # include < string > käyttäen nimiavaruutta std ; class Asiakirja { vektori < merkkijono > data ; julkinen : Asiakirja ( ) { data . reservi ( 100 ); // vähintään 100 riviä } void Insert ( int line , const string & str ) { if ( line <= data . size ( ) ) data . insert ( data . alkaa () + rivi , str ); muuten selvitä << "Virhe!" << endl ; } void Poista ( int line ) { if ( !( line > data . size ( ) ) ) data . erase ( data.begin ( ) + rivi ) ; muuten selvitä << "Virhe!" << endl ; } merkkijono & operaattori [] ( int x ) { palauttaa tiedot [ x ]; } void Näytä () { for ( int i = 0 ; i < data . size (); ++ i ) { cout << i + 1 << ". " << data [ i ] << endl ; } } }; class Komento { suojattu : Asiakirja * doc ; public : virtual ~ Komento () {} virtuaalinen void Suorita () = 0 ; virtuaalinen void unExecute () = 0 ; void setDocument ( Asiakirja * _doc ) { doc = _doc ; } }; class InsertCommand : public Command { int line ; string str ; public : InsertCommand ( int _line , const string & _str ): rivi ( _line ), str ( _str ) {} void Suorita () { doc -> Insert ( rivi , str ); } void unExecute () { doc -> Poista ( rivi ); } }; class Invoker { vektori < Komento *> Valmiskomennot ; Asiakirja doc ; Komento * komento ; public : void Insert ( int line , string str ) { komento = new InsertCommand ( rivi , str ); komento -> setDocument ( & doc ); komento -> Suorita (); DoneCommands . push_back ( komento ); } void Kumoa () { if ( DoneCommands . size () == 0 ) { cout << "Ei ole mitään peruutettavaa!" << endl ; } else { komento = DoneCommands . takaisin (); DoneCommands . pop_back (); komento -> peruuta (); // Älä unohda poistaa komentoa!!! poista komento ; } } void Näytä () { doc . näytä (); } }; int main () { char s = '1' ; int rivi , rivi_b ; string str ; Invoker inv ; while ( s != 'e' ) { cout << "Mitä tehdä: \n1.Lisää rivi\n2.Kumoa viimeinen komento" << endl ; cin >> s ; kytkin ( s ) { case '1' : cout << "Mikä rivi lisätään: " ; cin >> rivi ; --rivi ; _ cout << "Mitä lisätä:" ; cin >> str ; lasku . lisää ( rivi , str ); tauko ; tapaus '2' : inv . Kumoa (); tauko ; } cout << "$$$DOCUMENT$$$" << endl ; lasku . näytä (); cout << "$$$DOCUMENT$$$" << endl ; } }

Python- esimerkki

Lähdekoodi Pythonissa abc importista ABCMeta , abstrakti menetelmä luokan joukko : """ Vastaanotin - Joukkoobjekti """ def move ( self , direction : str ) -> None : """ Aloita liikkuminen tiettyyn suuntaan """ print ( 'Squad aloitti liikkeen {} ' . muoto ( suunta )) def stop ( itse ) -> Ei mitään : """ Pysäytä """ print ( 'Squad stop' ) class Komento ( metaclass = ABCMeta ): """ Perusluokka kaikille komentoille """ @abstractmethod def execute ( itse ) -> Ei mitään : """ Jatka komennon """ suorittamiseen @abstractmethod def unexecute ( self ) -> Ei mitään : """ Peruuta komento "" " luokan AttackCommand ( komento ): """ Hyökkäyksen suorittamiskomento on """ def __init__ ( itse , joukko : Joukko ) -> Ei mitään : """ Rakentaja. :param troop: joukko, johon komento " "" liittyy itse .troop = joukko def execute ( itse ) -> Ei mitään : itse . joukko . siirtää ( 'eteenpäin' ) def unexecute ( itse ) -> Ei mitään : itse . joukko . lopeta () luokka RetreatCommand ( komento ): """ Retreat-komento """ def __init__ ( itse , joukko : Joukko ) -> Ei mitään : """ Rakentaja. :param troop: joukko, johon komento """ itse liittyy . joukko = joukko def execute ( itse ) -> Ei mitään : itse . joukko . siirtää ( 'takaisin' ) def unexecute ( itse ) -> Ei mitään : itse . joukko . lopeta () luokka TroopInterface : """ Invoker - käyttöliittymä, jonka kautta voit antaa komentoja tietylle ryhmälle """ def __init__ ( itse , hyökkäys : AttackCommand , rereat : RetreatCommand ) -> Ei mitään : """ Rakentaja. :param attack: attack command :param retreat: perääntymiskomento " "" itse .attack_command = hyökkäys itse .retreat_command = vetäytyä itse .current_command = Ei mitään # komento käynnissä def attack ( itse ) -> Ei mitään : itse . nykyinen_komento = itse . attack_command self . attack_command . suorittaa () def retreat ( itse ) -> Ei mitään : itse . nykyinen_komento = itse . retreat_command self . retreat_command . suorittaa () def stop ( itse ) -> Ei mitään : jos itse . nykyinen_komento : itse . nykyinen_komento . peruuta () itse . current_command = Ei muuta : print ( 'Laite ei voi pysähtyä, koska se ei liiku' ) if __name__ == '__main__' : troop = Joukko () interface = JoukkoInterface ( AttackCommand ( joukko ), RetreatCommand ( joukko )) käyttöliittymä . hyökkäys () käyttöliittymä . stop () -liitäntä . rereat () käyttöliittymä . lopeta ()

PHP5 esimerkki

PHP5 lähdekoodi <?php /** * Abstract class "komennot" * @abstract */ abstrakti class Komento { public abstrakti toiminto Suorita (); julkinen abstrakti toiminto UnExecute (); } /** * Konkreettisen "komennon" luokka */ luokka CalculatorCommand laajenee Komento { /** * Nykyinen komentotoiminto * * @var string */ public $operator ; /** * Nykyinen operandi * * @var sekoitettu */ julkinen $operandi ; /** * Luokka, jota komento koskee * * @var objektin luokan Laskin */ public $calculator ; /** * Rakentaja * * @param- objekti $laskin * @param-merkkijono $operaattori * @param sekoitettu $operandi */ julkinen funktio __konstrukti ( $laskin , $operaattori , $operandi ) { $this -> calculator = $laskin ; $this -> operaattori = $operaattori ; $this -> operandi = $operandi ; } /** * Uudelleen toteutettu vanhempi::Suorita() -funktio */ julkinen funktio Execute () { $this -> calculator -> Operation ( $this -> operaattori , $this -> operandi ); } /** * Uudelleentoteutettu vanhempi::UnExecute() -funktio */ julkinen toiminto UnExecute () { $this -> calculator -> Operation ( $this -> Undo ( $this -> operaattori ), $this -> operandi ); } /** * Mikä toiminto pitäisi kumota? * * @private * @param string $operator * @return string */ yksityinen toiminto Kumoa ( $operator ) { //etsi jokaiselle suoritetulle toiminnolle käänteinen kytkin ( $operator ) { case '+' : $undo = '-' ; tauko ; case '-' : $undo = '+' ; tauko ; case '*' : $undo = '/' ; tauko ; case '/' : $undo = '*' ; tauko ; oletus : $undo = ' ' ; tauko ; } return $kumoa ; } } /** * Luokkavastaanotin ja "komentojen" suorittaja */ luokkalaskin { /** * Komennon suorituksen nykyinen tulos * * @private * @var int */ yksityinen $curr = 0 ; public function Toiminto ( $operaattori , $operandi ) { //valitse operaattori tulosvaihtimen laskemiseksi ( $operaattori ) { case '+' : $this -> curr += $operandi ; tauko ; case '-' : $this -> curr -= $operandi ; tauko ; case '*' : $this -> curr *= $operandi ; tauko ; case '/' : $this -> curr /= $operandi ; tauko ; } print ( "Nykyinen tulos = $this->curr (jonka jälkeen $operator c $operand )" ); } } /** * Komentoja kutsuva luokka */ luokka Käyttäjä { /** * Tämä luokka vastaanottaa suoritettavat komennot * * @private * @var-luokan objekti Laskin */ yksityinen $laskin ; /** * Joukko operaatioita * * @private * @var array */ yksityinen $komennot = array (); /** * Nykyinen komento operaatiotaulukossa * * @private * @var int */ yksityinen $current = 0 ; public function __construct () { // luo luokan esiintymä, joka suorittaa komennot $this -> calculator = new Laskin (); } /** * Toiminto peruutettujen komentojen palauttamiseksi * * @param int $levels palautettavien operaatioiden määrä */ public function Toista ( $levels ) { print ( " \n ---- Toista $levels - operaatiot " ); // Palautustoiminnot komennon ( $i = 0 ; $i < $tasot ; $i ++ ) if ( $this -> nykyinen < count ( $this -> komennot ) - 1 ) $this -> komennot [ $this - > nykyinen ++ ] -> Suorita (); } /** * Komento undo function * * @param int $levels kumoamistoimintojen määrä */ public function Kumoa ( $levels ) { print ( " \n ---- Kumoa $tasot " ); // Kumoa toiminnot kohteelle ( $i = 0 ; $i < $tasot ; $i ++ ) if ( $this -> current > 0 ) $this -> komennot [ -- $this -> current ] -> UnExecute ( ); } /** * Komennon suoritustoiminto * * @param string $ operand * @param sekoitettu $operand */ julkinen toiminto Laske ( $operaattori , $operandi ) { // Luo operaatiokomento ja suorita se $komento = new CalculatorCommand ( $this -> laskin , $operaattori , $operandi ); $komento -> Suorita (); // Lisää operaatio joukkoon operaatio ja lisää nykyisen operaation laskuria $this -> commands [] = $command ; $this -> nykyinen ++ ; } } $käyttäjä = uusi käyttäjä (); // Mielivaltaiset komennot $user -> Compute ( '+' , 100 ); $user -> Compute ( '-' , 50 ); $käyttäjä -> Laske ( '*' , 10 ); $käyttäjä -> Laske ( '/' , 2 ); // Kumoa 4 komentoa $user -> Kumoa ( 4 ); // Palauta 3 peruutettua komentoa. $käyttäjä -> Tee uudelleen ( 3 );

Java- esimerkki

Java lähde

Toteuttaakseen operaatioiden nimien vastaavuuden toimintoon, lampun toiminnot (sytytys, sammutus) siirretään luokkien esiintymään SwitchOnCommandja SwitchOffCommandmolemmat luokat toteuttavat rajapinnan Command.

tuonti java.util.HashMap ; /** Komentoliittymä */ käyttöliittymä Komento { void execute (); } /** Invoker-luokka */ luokkakytkin { yksityinen lopullinen HashMap < String , Command > commandMap = new HashMap < > (); public void register ( String commandName , Command command ) { commandMap . put ( komentoNimi , komento ); } public void execute ( String commandName ) { Command command = commandMap . get ( komentoNimi ); if ( komento == null ) { heitto uusi IllegalStateException ( "komentoa ei ole rekisteröity kohteelle " + komennonNimi ); } komento . suorittaa (); } } /** Vastaanottimen luokka */ luokka Valo { public void turnOn () { System . ulos . println ( "Valo palaa" ); } public void turnOff () { Järjestelmä . ulos . println ( "Valo ei pala" ); } } /** Valon sytyttämiskomento - ConcreteCommand #1 */ luokka SwitchOnCommand toteuttaa Komento { yksityinen lopullinen Light light ; public SwitchOnCommand ( Light light ) { this . valo = valo ; } @Override // Komento public void execute () { valo . laita päälle (); } } /** Valon sammutuskomento - ConcreteCommand #2 */ luokka SwitchOffCommand toteuttaa komennon { yksityinen lopullinen Light light ; public SwitchOffCommand ( Light Light ) { this . valo = valo ; } @Override // Komento public void execute () { valo . sammuttaa (); } } public class CommandDemo { public static void main ( viimeinen merkkijono [] argumentit ) { Light lamp = new Light (); Command switchOn = uusi SwitchOnCommand ( lamppu ); Komento pois päältä = uusi SwitchOffCommand ( lamppu ); Switch mySwitch = uusi Kytkin (); mySwitch . rekisteröidy ( "on" , switchOn ); mySwitch . rekisteröidy ( "off" , switchOff ); mySwitch . suorittaa ( "päällä" ); mySwitch . suorittaa ( "off" ); } } Käytä toiminnallista käyttöliittymää

Java 8:sta alkaen luokkien luominen ei ole pakollista SwitchOnCommandja SwitchOffCommandsen sijaan voimme käyttää operaattoria ::seuraavan esimerkin mukaisesti

public class CommandDemo { public static void main ( viimeinen merkkijono [] argumentit ) { Light lamp = new Light (); KomentokytkinPäälle = lamppu :: päälle ; _ Komento sammutus = lamppu :: sammuta ; Switch mySwitch = uusi Kytkin (); mySwitch . rekisteröidy ( "on" , switchOn ); mySwitch . rekisteröidy ( "off" , switchOff ); mySwitch . suorittaa ( "päällä" ); mySwitch . suorittaa ( "off" ); } }

Swift 5 esimerkki

Lähdekoodi Swift 5:ssä protokolla Komento { Func execute() } // soittaja luokan vaihto { enum SwitchAction { kotelo päällä, pois päältä } var status: merkkijono? var toiminta: kevyt? func register(_ komento: Light) { self.action = komento } func execute(_ commandName: SwitchAction) { if commandName == .on { toiminta?.turnOn() } else if commandName == .off { toiminta?.turnOff() } } } // Vastaanotin luokka valo { func turnOn() { print ("Valo palaa") } func turnOff() { print("Valo ei pala") } } class SwitchOnCommand: Komento { yksityinen var valo: valo init(_light: Light) { itse.valo = valo } func execute() { light.turnOn() } } class SwitchOffCommand: Komento { yksityinen var valo: valo init(_light: Light) { itse.valo = valo } func execute() { light.turnOff() } } // KÄYTÄ anna invoker = Switch() anna vastaanotin = valo() invoker.register(receiver) invoker.execute(.on)

Rubiiniesimerkki _

Rubyn lähdekoodi moduuli EngineCommands # Abstrakti luokka 'Command' luokka Komento def execute end end # Vastaanotinluokka Moottori attr_reader : state def alustaa rpm @state , @rpm = false , rpm if rpm . on? Kokonaisluvun loppu def turnOn ; @tila = tosi ; end def turnOff ; @tila = false ; loppu loppu # ConcreteCommand1 luokka CommandTurnOn < Komento def alusta moottori @engine = moottori jos moottori . on? moottorin pää def execute @engine . turnOn end pää # ConcreteCommand2 luokka CommandTurnOff < Komento def alusta moottori @engine = moottori jos moottori . on? moottorin pää def execute @engine . sammuta loppupää _ # Invoker- luokka Invoker def alusta @komennot = Hash . uusi loppu def registerKomento komentoNimi , komento @komennot [ komennonNimi ] = komento jos komento . on? Komento ja @komennot [ commandName ]. on? NilClass loppu def executeCommand commandName @command = @komennot [ commandName ] ellei @command . on? NilClass @command . suorita else nosta TypeError . uusi loppu loppu loppu loppu # Asiakasmoduuli Client sisältää EngineCommands invoker = Kutsuja . Uusi moottori = moottori . uusi ( 250 ) commandTurnOn = CommandTurnOn . new ( engine ) commandTurnOff = CommandTurnOff . uusi ( moottori ) kutsuja . registerKomento "turnOn" , komentoTurnOn invoker . registerKomento "turnOff" , komentoTurnOff asettaa " \t Moottorin tila ennen komennon käyttöä: #{ engine . state } " # => Moottorin tila ennen komennon käyttöä: false asettaa " \t Moottorin tila komennon 'turnOn' käytön jälkeen: #{ invoker . executeCommand "turnOn" } " # => Moottorin tila komennon 'turnOn' käytön jälkeen: true asettaa " \t Moottorin tila käytön jälkeen komennon 'turnOff': #{ invoker . executeCommand "turnOff" } " # => Moottorin tila käytön jälkeen komento 'turnOff': false end

Linkit