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 .
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 ; } }
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 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