Kommando (designmönster)
Den aktuella versionen av sidan har ännu inte granskats av erfarna bidragsgivare och kan skilja sig väsentligt från
versionen som granskades den 20 september 2019; kontroller kräver
8 redigeringar .
Ett kommando är ett beteendedesignmönster som används i objektorienterad programmering som representerar en handling. Kommandoobjektet innehåller själva åtgärden och dess parametrar.
Syfte
Skapa en struktur där avsändarklassen och mottagarklassen inte är direkt beroende av varandra. Organisera en återuppringning till en klass som inkluderar avsändarklassen.
Beskrivning
I objektorienterad programmering är kommandodesignmönstret ett beteendemönster där ett objekt används för att kapsla in all information som behövs för att utföra en åtgärd eller ta upp en händelse vid ett senare tillfälle. Denna information inkluderar namnet på metoden, objektet som äger metoden och värdena för metodens parametrar.
Fyra termer är alltid associerade med kommandomönstret: kommandon (kommando), kommandomottagare (mottagare), kommandoanropare (anropare) och klient (klient). Kommandoobjektet känner till mottagaren och anropar mottagarens metod. Mottagarens parametervärden lagras i kommandot. Den som ringer (anroparen) vet hur man utför kommandot och håller eventuellt reda på de utförda kommandona. Den som ringer (anroparen) vet ingenting om ett visst kommando, den vet bara om gränssnittet. Båda objekten (det anropande objektet och flera kommandoobjekt) tillhör klientobjektet. Klienten bestämmer vilka kommandon som ska utföras och när. För att utföra ett kommando skickar den kommandoobjektet till den som ringer (anroparen).
Att använda kommandoobjekt gör det enkelt att bygga delade komponenter som du behöver för att delegera eller göra metodanrop när som helst utan att behöva känna till klassmetoderna eller metodparametrarna. Genom att använda caller-objektet (invoker) kan du hålla ett register över utförda kommandon utan att kunden behöver känna till denna redovisningsmodell (en sådan redovisning kan vara användbar, till exempel för att implementera kommandon ångra och gör om).
Applikation
Kommandomönstret kan vara användbart i följande fall.
Användargränssnittsknappar och menyalternativ
I Swing och Borland Delphi är en Action ett kommandoobjekt. Förutom att kunna utföra önskat kommando kan en åtgärd ha en tillhörande ikon, kortkommando, verktygstipstext och så vidare. En knapp eller ett menyalternativ i verktygsfältet kan initieras helt med endast ett åtgärdsobjekt .
Makroinspelning
Om alla användaråtgärder representeras som kommandoobjekt kan programmet spela in en sekvens av åtgärder genom att helt enkelt lagra en lista med kommandoobjekt i den ordning som de utförs. Den kan sedan "spela om" samma åtgärder genom att utföra samma kommandoobjekt i samma sekvens.
Ångra på flera nivåer ( Ångra )
Om alla användaråtgärder i programmet implementeras som kommandoobjekt, kan programmet spara en stapel av de senast utförda kommandona. När användaren vill avbryta ett kommando, öppnar programmet helt enkelt det sista kommandoobjektet och kör dess undo()- metod .
nätverk
Du kan skicka kommandoobjekt över nätverket för att köras på en annan maskin, till exempel en spelaråtgärd i ett datorspel.
Framstegsindikatorer
Anta att ett program har en sekvens av kommandon som det kör i ordning. Om varje kommandoobjekt har en getEstimatedDuration()- metod kan programmet enkelt uppskatta den totala varaktigheten av processen. Det kan visa en förloppsindikator som visar hur nära programmet är att slutföra alla uppgifter.
Trådpooler
En typisk trådpoolsklass för allmänt bruk kan ha en addTask()- metod som lägger till ett arbetsobjekt i en intern kö av väntande uppgifter. Den upprätthåller en pool av trådar som kör kommandon från en kö. Elementen i kön är kommandoobjekt. Vanligtvis implementerar dessa objekt ett gemensamt gränssnitt som java.lang.Runnable , vilket gör att trådpoolen kan köra kommandon även om den skrevs utan någon som helst kunskap om de specifika uppgifter som den kommer att användas för.
Transaktioner
I likhet med "ångra" -operationen kan ett databashanteringssystem (DBMS) eller programvaruinstallationsprogram lagra en lista över operationer som har utförts eller kommer att utföras. Om en av dem misslyckas, kan alla andra avbrytas eller kasseras (kallas vanligen en rollback). Till exempel, om två relaterade databastabeller behöver uppdateras och den andra uppdateringen misslyckas, kan systemet återställa transaktionen så att den första tabellen inte innehåller en ogiltig länk.
Mästare
Ofta presenterar en guide (installationsguide eller vad som helst) flera konfigurationssidor för en enda åtgärd som bara händer när användaren klickar på "Slutför"-knappen på sista sidan. I dessa fall är det naturliga sättet att separera användargränssnittskoden från applikationskoden att implementera guiden med ett kommandoobjekt. Kommandoobjektet skapas första gången guiden visas. Varje guidesida sparar sina ändringar i kommandoobjektet, så att objektet fylls i när användaren navigerar. "Klar"-knappen utlöser helt enkelt metoden execute() att köra.
Exempel
C++ exempel
Källtext i C++
# include < iostream >
# include < vektor >
# include < string >
använder namnutrymme std ;
klass Dokument
{
vektor < sträng > data ;
public :
Document ()
{
data . reserv ( 100 ); // åtminstone för 100 rader
}
void Infoga ( int line , const string & str )
{
if ( line <= data . size () )
data . infoga ( data . börja () + rad , str );
else
cout << "Fel!" << endl ;
}
void Ta bort ( int line )
{
if ( !( line > data . size () ) ) )
data . radera ( data.begin ( ) + rad ) ; else cout << "Fel!" << endl ; }
sträng & operator [] ( int x )
{
returnera data [ x ];
}
void Visa ()
{
for ( int i = 0 ; i < data . size (); ++ i )
{
cout << i + 1 << ". " << data [ i ] << endl ;
}
}
};
class Kommando
{
protected :
Document * doc ;
public :
virtual ~ Kommando () {}
virtual void Execute () = 0 ;
virtual void unExecute () = 0 ;
void setDocument ( Dokument * _doc )
{
doc = _doc ;
}
};
class InsertCommand : public Command
{
int line ;
sträng str ;
public :
InsertCommand ( int _line , const string & _str ): line ( _line ), str ( _str ) {}
void Execute ()
{
doc -> Insert ( line , str );
}
void unExecute ()
{
doc -> Remove ( line );
}
};
class Invoker
{
vektor < Kommando *> DoneCommands ;
Dokument doc ;
Kommando * kommando ;
public :
void Insert ( int line , string str )
{
command = new InsertCommand ( line , str );
kommando -> setDocument ( & doc );
kommando -> Kör ();
Klarkommandon . push_back ( kommando );
}
void Undo ()
{
if ( DoneCommands . size () == 0 )
{
cout << "Det finns inget att ångra!" << endl ;
}
else
{
command = DoneCommands . tillbaka ();
Klarkommandon . pop_back ();
kommando -> unexecute ();
// Glöm inte att radera kommandot!!!
radera kommando ;
}
}
void Visa ()
{
doc . visa ();
}
};
int main ()
{
char s = '1' ;
int linje , linje_b ;
sträng str ;
Invoker inv ;
while ( s != 'e' )
{
cout << "Vad man ska göra: \n1.Lägg till en rad\n2. Ångra senaste kommandot" << endl ;
cin >> s ;
switch ( s )
{
case '1' :
cout << "Vilken rad att infoga: " ;
cin >> linje ;
--linje ; _ cout << "Vad ska infogas: " ; cin >> str ; inv . infoga ( linje , str ); bryta ; fall '2' : inv . Ångra (); bryta ; } cout << "$$$DOCUMENT$$$" << endl ; inv . visa (); cout << "$$$DOCUMENT$$$" << endl ; } }
Källkod i Python
från abc import ABCMeta , abstraktmetod
klass Trupp :
"""
Mottagare - Truppobjekt
"""
def move ( self , direction : str ) -> None :
"""
Börja röra sig i en viss riktning
"""
print ( 'Squad började röra sig {} ' . format ( direction ))
def stop ( self ) -> None :
"""
Stop
"""
print ( 'Squad stoped' )
class Command ( metaclass = ABCMeta ):
"""
Basklass för alla kommandon
"""
@abstractmethod
def execute ( self ) -> None :
"""
Fortsätt för att köra kommandot
"""
pass
@abstractmethod
def unexecute ( self ) -> Ingen :
"""
Unexecute kommando
"""
passera
class AttackCommand ( Kommando ):
"""
Kommandot för att utföra attacken
är """
def __init__ ( själv , trupp : Trupp ) -> Ingen :
"""
Konstruktör.
:param troop: truppen som kommandot
" "" är associerad med
self .troop = trupp
def execute ( self ) -> None :
self . trupp . flytta ( 'framåt' )
def unexecute ( själv ) -> Ingen :
själv . trupp . sluta ()
class RetreatCommand ( Kommando ):
"""
Retreat-kommando
"""
def __init__ ( self , troop : Troop ) -> Ingen :
"""
Konstruktör.
:param troop: truppen som kommandot """ self är associerat med . trupp = trupp
def execute ( self ) -> None :
self . trupp . flytta ( 'tillbaka' )
def unexecute ( själv ) -> Ingen :
själv . trupp . sluta ()
class TroopInterface :
"""
Invoker - ett gränssnitt genom vilket du kan utfärda kommandon till en specifik grupp
"""
def __init__ ( själv , attack : AttackCommand , retreat : RetreatCommand ) -> Ingen :
"""
Konstruktör.
:param attack: attack kommando
:param retreat: retreat kommando
" ""
self .attack_command = attack self .retreat_command = retreat self .current_command = Inget # kommando körs för närvarande
def attack ( själv ) -> Ingen :
själv . aktuell_kommando = själv . attack_command
self . attack_command . exekvera ()
def retreat ( själv ) -> Ingen :
själv . aktuell_kommando = själv . retreat_command
self . retreat_command . exekvera ()
def stop ( self ) -> Ingen :
om själv . nuvarande_kommando :
själv . aktuellt_kommando . unexecute ()
self . current_command = Inga
andra :
print ( 'Enheten kan inte stanna eftersom den inte rör sig' )
om __name__ == '__main__' :
trupp = Trupp ()
gränssnitt = TroopInterface ( AttackCommand ( trupp ), RetreatCommand ( trupp ))
gränssnitt . attack ()
gränssnitt . stoppa ()
gränssnitt . reträtt ()
gränssnitt . sluta ()
PHP5 källkod
<?php
/**
* Abstrakt klass "kommandon"
* @abstract
*/
abstrakt klass Kommando
{
public abstract function Execute ();
offentlig abstrakt funktion UnExecute ();
}
/**
* Klassen för det konkreta "kommandot"
*/
klass CalculatorCommand utökar Command
{
/**
* Aktuell kommandooperation
*
* @var string
*/
public $operator ;
/**
* Aktuell operand
*
* @var blandad
*/
offentlig $operand ;
/**
* Klassen kommandot är för
*
* @var objekt i klassen Calculator
*/
public $calculator ;
/**
* Konstruktör
*
* @param objekt $
calculator * @param sträng $operator
* @param blandad $operand
*/
offentlig funktion __construct ( $ calculator , $operator , $operand )
{
$this -> calculator = $calculator ;
$this -> operator = $operator ;
$this -> operand = $operand ;
}
/**
* Återimplementerad överordnad::Execute() funktion
*/
public function Execute ()
{
$this -> kalkylator -> Operation ( $this -> operator , $this -> operand );
}
/**
* Återimplementerad förälder::UnExecute() funktion
*/
public function UnExecute ()
{
$this -> kalkylator -> Operation ( $this -> Undo ( $this -> operator ), $this -> operand );
}
/**
* Vilken åtgärd ska ångras?
*
* @private
* @param string $operator
* @return string
*/
privat funktion Ångra ( $operator )
{
//hitta det omvända för varje åtgärd som utförs
switch ( $operator )
{
case '+' : $undo = '-' ; bryta ;
case '-' : $undo = '+' ; bryta ;
case '*' : $undo = '/' ; bryta ;
case '/' : $undo = '*' ; bryta ;
default : $undo = ' ' ; bryta ;
}
returnera $ångra ;
}
}
/**
* Klassmottagare och utförare av "kommandon"
*/
class Calculator
{
/**
* Aktuellt resultat av kommandoexekvering
*
*
@private * @var int
*/
privat $curr = 0 ;
public function Operation ( $operator , $operand )
{
//välj operator för att beräkna resultatomkopplare
( $ operator )
{
case '+' : $this -> curr += $operand ; bryta ;
case '-' : $this -> curr -= $operand ; bryta ;
case '*' : $this -> curr *= $operand ; bryta ;
case '/' : $this -> curr /= $operand ; bryta ;
}
print ( "Aktuellt resultat = $this->curr (efter exekvering av $operator c $operand )" );
}
}
/**
* Klass som anropar kommandon
*/
class User
{
/**
* Den här klassen kommer att få kommandon som ska köras
*
*
@private * @var objekt i klassen Calculator
*/
private $calculator ;
/**
* Array of operations
*
*
@private * @var array
*/
private $commands = array ();
/**
* Aktuellt kommando i operationsarray
*
*
@private * @var int
*/
privat $current = 0 ;
public function __construct ()
{
// skapa en instans av klassen som kommer att utföra kommandon
$this -> calculator = new Calculator ();
}
/**
* Funktion för att returnera avbrutna kommandon
*
* @param int $levels antal operationer att returnera
*/
public function Gör om ( $levels )
{
print ( " \n ---- Repeat $levels operations " );
// Returnera operationer
för ( $i = 0 ; $i < $nivåer ; $i ++ )
if ( $this -> nuvarande < count ( $this -> kommandon ) - 1 )
$this -> kommandon [ $this - > aktuell ++ ] -> Kör ();
}
/**
* Kommando ångra funktion
*
* @param int $levels antal ångra operationer
*/
offentlig funktion Ångra ( $levels )
{
print ( " \n ---- Ångra $levels operationer " );
// Ångra operationer
för ( $i = 0 ; $i < $nivåer ; $i ++ )
if ( $this -> current > 0 )
$this -> kommandon [ -- $this -> current ] -> UnExecute ( );
}
/**
* Kommandoexekveringsfunktion
*
* @param sträng $operator
* @param blandad $operand
*/
public function Compute ( $operator , $operand )
{
// Skapa ett operationskommando och kör det
$command = new CalculatorCommand ( $this - > kalkylator , $operator , $operand );
$command -> Execute ();
// Lägg till en operation till arrayen av operationer och öka räknaren för den aktuella operationen
$this -> kommandon [] = $kommando ;
$this -> nuvarande ++ ;
}
}
$user = ny användare ();
// Godtyckliga kommandon
$user -> Compute ( '+' , 100 );
$user -> Compute ( '-' , 50 );
$user -> Beräkna ( '*' , 10 );
$user -> Beräkna ( '/' , 2 );
// Ångra 4 kommandon
$user -> Undo ( 4 );
// Returnera 3 avbrutna kommandon.
$user -> Gör om ( 3 );
Java-källa
För att implementera överensstämmelsen mellan operationernas namn och åtgärden flyttas operationerna på lampan (slå på, släcka) till en instans av klasser SwitchOnCommandoch SwitchOffCommandbåda klasserna implementerar gränssnittet Command.
importera java.util.HashMap ;
/** Kommandogränssnittet */
interface Kommando {
void execute ();
}
/** Invoker-klassen */
class Switch {
private final HashMap < String , Command > commandMap = new HashMap <> ();
public void register ( String commandName , Command command ) {
commandMap . put ( kommandonamn , kommando );
}
public void exekvera ( String commandName ) {
Command command = commandMap . get ( kommandonamn );
if ( kommando == null ) {
throw new IllegalStateException ( "inget kommando registrerat för " + commandName );
}
kommandot . exekvera ();
}
}
/** Mottagareklassen */
class Light {
public void turnOn () {
System . ut . println ( "Ljuset är på" );
}
public void avstängning () {
System . ut . println ( "Ljuset är släckt" );
}
}
/** Kommandot för att tända ljuset - ConcreteCommand #1 */
class SwitchOnCommand implementerar Command {
private final Light light ;
public SwitchOnCommand ( Ljus ljus ) {
detta . ljus = ljus ;
}
@Override // Kommando
public void execute () {
light . slå på ();
}
}
/** Kommandot för att släcka ljuset - ConcreteCommand #2 */
class SwitchOffCommand implementerar kommandot {
private final Light light ;
public SwitchOffCommand ( Ljus ljus ) {
detta . ljus = ljus ;
}
@Override // Kommando
public void execute () {
light . avstängning ();
}
}
public class CommandDemo {
public static void main ( final String [] arguments ) {
Light lamp = new Light ();
Kommando switchOn = nytt SwitchOnCommand ( lampa );
Kommando switchOff = nytt SwitchOffCommand ( lampa );
Switch mySwitch = new Switch ();
mySwitch . register ( "på" , slå på );
mySwitch . register ( "av" , stängAv );
mySwitch . exekvera ( "på" );
mySwitch . exekvera ( "av" );
}
}
Använda det funktionella gränssnittet
Från och med Java 8 är det inte obligatoriskt att skapa klasser SwitchOnCommandoch SwitchOffCommandistället kan vi använda en operator ::som visas i följande exempel
public class CommandDemo {
public static void main ( final String [] arguments ) {
Light lamp = new Light ();
Kommando switchOn = lamp :: turnOn ;
Kommando switchOff = lamp :: avstängning ;
Switch mySwitch = new Switch ();
mySwitch . register ( "på" , slå på );
mySwitch . register ( "av" , stängAv );
mySwitch . exekvera ( "på" );
mySwitch . exekvera ( "av" );
}
}
Swift 5 exempel
Källkod i Swift 5
protokoll Kommando {
func execute()
}
// ringer
klass Switch {
enum SwitchAction {
fodral på, av
}
var status: String?
var action: Ljus?
func register(_ kommando: Ljus) {
self.action = kommando
}
func execute(_ commandName: SwitchAction) {
if commandName == .on {
action?.turnOn()
} else if commandName == .off {
action?.turnOff()
}
}
}
// Mottagare
klass ljus {
func turnOn() {
print ("Ljuset lyser")
}
func turnOff() {
print ("Ljuset är AV")
}
}
class SwitchOnCommand: Kommando {
privat var ljus: Ljus
init(_light: Light) {
själv.ljus = ljus
}
func execute() {
light.turnOn()
}
}
class SwitchOffCommand: Kommando {
privat var ljus: Ljus
init(_light: Light) {
själv.ljus = ljus
}
func execute() {
light.turnOff()
}
}
// ANVÄNDA SIG AV
låt invoker = Switch()
låt mottagare = Light()
invoker.register(mottagare)
invoker.execute(.on)
Ruby källkod
modul EngineCommands
# Abstrakt klass 'Command'
- klass Kommando
def exekvera
slutet
# Mottagareklass
Engine attr_reader : state
def initialisera rpm
@state , @rpm = false , rpm if rpm . är en? Heltalsslut
_
def slå på ; @tillstånd = sant ; slut
def avstängning ; @state = falskt ; slutändan
_
# ConcreteCommand1
klass CommandTurnOn < Kommando
def initialisera motor
@engine = motor om motor . är en? motoränden
_
def exekvera
@engine . slå
på slutet
# ConcreteCommand2
klass CommandTurnOff < Kommando
def initialisera motor
@motor = motor om motor . är en? motoränden
_
def exekvera
@engine . stäng
av änden
# Invoker-
klass Invoker
def initialize
@commands = Hash . nytt
slut
def registerCommand kommandonamn , kommando
@kommandon [ kommandonamn ] = kommandot if kommando . är en? Kommando och @kommandon [ kommandonamn ]. är en? Ingen klass
slut
def executeCommand kommandonamn @kommando =
@kommandon [ kommandonamn ] om inte @kommando . är en? NilClass @kommando . exekvera annars höj TypeError . ny ände ände ände _
#
Klientmodul Klienten
inkluderar EngineCommands
invoker = Invoker . ny
motor = motor . ny ( 250 )
commandTurnOn = CommandTurnOn . ny ( motor )
commandTurnOff = CommandTurnOff . ny ( motor )
anropare . registerCommand "turnOn" , commandTurnOn
invoker . registerCommand "turnOff" , kommandoTurnOff
sätter " \t Engine State före användning av kommandot: #{ engine . state } " # => Engine State före användning av kommandot: false
sätter " \t Engine State efter användning av kommandot 'turnOn': #{ invoker . executeCommand "turnOn" } " # => Motortillstånd efter användning av kommandot 'turnOn': true
sätter " \t Engine State after use kommando 'turnOff': #{ invoker . executeCommand "turnOff" } " # => Motortillstånd efter användning kommando 'turnOff': false
end
Länkar