命令
命令模式是一種將傳送者和接收者解耦的物件行為模式。它也可以被認為是回撥方法的面向物件等價物。回撥:它是在使用者操作後註冊在稍後時間呼叫的函式。
範圍
物件
目的
行為
意圖
將服務請求封裝成一個物件。
適用性
- 用要執行的操作引數化物件
- 在不同時間指定、排隊和執行請求
- 用於請求歷史
- 用於多級撤銷/重做
結構

後果
- + 抽象服務執行者
- + 支援任意級別的撤銷/重做
- + 組合產生宏命令
- - 可能會導致大量瑣碎的命令子類
例子

此模式的最佳示例是圖形使用者介面。在大多數多資料介面上,您都有“新建”選單和“新建”按鈕,帶有類似 Libre Office Writer 中的圖示。這兩個控制元件都連線到一個命令物件,因此操作由相同的程式碼執行。
成本
此模式處理整個程式的架構。它可能會對專案產生重大影響。
建立
如果程式碼已經存在,此模式非常昂貴。
維護
此模式維護起來非常昂貴。
移除
此模式移除起來也非常昂貴。
建議
- 使用命令術語來向其他開發人員表明使用該模式。
實現
考慮一個“簡單”開關。在這個例子中,我們用兩個命令配置開關:開啟燈和關閉燈。此命令模式實現的一個好處是,開關可以用於任何裝置,而不僅僅是燈——以下示例中的開關開啟和關閉燈,但開關的建構函式能夠接受其兩個引數的任何 Command 子類。例如,您可以配置開關來啟動發動機。
/* The Command interface */
public interface Command {
void execute();
}
import java.util.List;
import java.util.ArrayList;
/* The Invoker class */
public class Switch {
private List<Command> history = new ArrayList<Command>();
public Switch() {
}
public void storeAndExecute(Command cmd) {
this.history.add(cmd); // optional
cmd.execute();
}
}
/* The Receiver class */
public class Light {
public Light() {
}
public void turnOn() {
System.out.println("The light is on");
}
public void turnOff() {
System.out.println("The light is off");
}
}
/* The Command for turning on the light - ConcreteCommand #1 */
public class FlipUpCommand implements Command {
private Light theLight;
public FlipUpCommand(Light light) {
this.theLight = light;
}
public void execute(){
theLight.turnOn();
}
}
/* The Command for turning off the light - ConcreteCommand #2 */
public class FlipDownCommand implements Command {
private Light theLight;
public FlipDownCommand(Light light) {
this.theLight = light;
}
public void execute() {
theLight.turnOff();
}
}
/* The test class or client */
public class PressSwitch {
public static void main(String[] args){
// Check number of arguments
if (args.length != 1) {
System.err.println("Argument \"ON\" or \"OFF\" is required.");
System.exit(-1);
}
Light lamp = new Light();
Command switchUp = new FlipUpCommand(lamp);
Command switchDown = new FlipDownCommand(lamp);
// See criticism of this model above:
// The switch itself should not be aware of lamp details (switchUp, switchDown)
// either directly or indirectly
Switch mySwitch = new Switch();
switch (args[0]) {
case "ON":
mySwitch.storeAndExecute(switchUp);
break;
case "OFF":
mySwitch.storeAndExecute(switchDown);
break;
default:
System.err.println("Argument \"ON\" or \"OFF\" is required.");
System.exit(-1);
}
}
}
操作
要做的實現是
- 在將命令放入歷史列表之前複製命令
- 處理滯後
- 支援事務
public interface Command {
public int execute(int a, int b);
}
public class AddCommand implements Command {
public int execute(int a, int b) {
return a + b;
}
}
public class MultCommand implements Command {
public int execute(int a, int b) {
return a * b;
}
}
public class TestCommand {
public static void main(String a[]) {
Command add = new AddCommand();
add.execute(1, 2); // returns 3
Command multiply = new MultCommand();
multiply.execute(2, 3); // returns 6
}
}
在上面的例子中,可以注意到命令模式將呼叫操作的物件與具有執行操作知識的物件解耦。
Java 中的命令
選單和按鈕提供了對命令模式需求的經典示例。當您嚮應用程式新增選單時,您必須用描述使用者可以選擇的操作的單詞來配置選單,例如儲存和開啟。按鈕也是如此。您還必須配置選單或按鈕,以便它能夠採取行動,在響應使用者點選時呼叫一個方法。但是,JMenuItem 或 JButton 類沒有辦法知道在選擇專案或按鈕時要執行什麼操作。在下面的示例中,我們對選單項和按鈕使用相同的 Action,這使我們不必編寫兩次相同的程式碼。
Action actionOpen = new AbstractAction("Open...", iconOpen) {
public void actionPerformed(ActionEvent e) {
... // open a file
}
}
JMenu mFile = new JMenu("File");
JMenuItem item = mFile.add(actionOpen); // use the same action for both a menu item ...
JToolBar m_toolBar = new JToolBar();
JButton bOpen = new JButton(actionOpen); // ... and a button
m_toolBar.add(bOpen);
Java Swing 應用程式通常應用中介者模式,註冊一個接收所有 GUI 事件的單個物件。此物件調解元件的互動,並將使用者輸入轉換為業務領域物件的命令。
Java 中的撤銷
Swing 提供了撤銷/重做功能。
import javax.swing.undo.*;
UndoManager undoManager = new UndoManager();
Action undoAction, redoAction;
undoAction = new AbstractAction("Undo",
new ImageIcon("edit_undo.gif")) {
public void actionPerformed(ActionEvent e) {
try {
undoManager.undo(); // undoManager.redo();
}
catch (CannotUndoException ex) {
System.err.println("Unable to undo: " + ex);
}
updateUndo();
}
};
// same for Redo
protected void updateUndo() {
if(undo.canUndo()) {
undoAction.setEnabled(true);
undoAction.putValue(Action.NAME, undo.getUndoPresentationName());
} else {
undoAction.setEnabled(false);
undoAction.putValue(Action.NAME, "Undo");
}
if(undo.canRedo()) {
redoAction.setEnabled(true);
redoAction.putValue(Action.NAME, undo.getRedoPresentationName());
} else {
redoAction.setEnabled(false);
redoAction.putValue(Action.NAME, "Redo");
}
}
考慮一個“簡單”開關。在這個例子中,我們用兩個命令配置開關:開啟燈和關閉燈。
此命令模式實現的一個好處是,開關可以用於任何裝置,而不僅僅是燈。以下 C# 實現中的開關開啟和關閉燈,但開關的建構函式能夠接受其兩個引數的任何 Command 子類。例如,您可以配置開關來啟動發動機。
using System;
namespace CommandPattern;
public interface ICommand
{
void Execute();
}
/* The Invoker class */
public class Switch
{
ICommand _closedCommand;
ICommand _openedCommand;
public Switch(ICommand closedCommand, ICommand openedCommand)
{
_closedCommand = closedCommand;
_openedCommand = openedCommand;
}
// Close the circuit / power on
public void Close()
{
_closedCommand.Execute();
}
// Open the circuit / power off
public void Open()
{
_openedCommand.Execute();
}
}
/* An interface that defines actions that the receiver can perform */
public interface ISwitchable
{
void PowerOn();
void PowerOff();
}
/* The Receiver class */
public class Light : ISwitchable
{
public void PowerOn()
{
Console.WriteLine("The light is on");
}
public void PowerOff()
{
Console.WriteLine("The light is off");
}
}
/* The Command for turning off the device - ConcreteCommand #1 */
public class CloseSwitchCommand : ICommand
{
private ISwitchable _switchable;
public CloseSwitchCommand(ISwitchable switchable)
{
_switchable = switchable;
}
public void Execute()
{
_switchable.PowerOff();
}
}
/* The Command for turning on the device - ConcreteCommand #2 */
public class OpenSwitchCommand : ICommand
{
private ISwitchable _switchable;
public OpenSwitchCommand(ISwitchable switchable)
{
_switchable = switchable;
}
public void Execute()
{
_switchable.PowerOn();
}
}
/* The test class or client */
internal class Program
{
public static void Main(string[] arguments)
{
string argument = arguments.Length > 0 ? arguments[0].ToUpper() : null;
ISwitchable lamp = new Light();
// Pass reference to the lamp instance to each command
ICommand switchClose = new CloseSwitchCommand(lamp);
ICommand switchOpen = new OpenSwitchCommand(lamp);
// Pass reference to instances of the Command objects to the switch
Switch @switch = new Switch(switchClose, switchOpen);
if (argument == "ON")
{
// Switch (the Invoker) will invoke Execute() on the command object.
@switch.Open();
}
else if (argument == "OFF")
{
// Switch (the Invoker) will invoke the Execute() on the command object.
@switch.Close();
}
else
{
Console.WriteLine("Argument \"ON\" or \"OFF\" is required.");
}
}
}
以下程式碼是 C# 中命令模式的另一個實現。
using System;
using System.Collections.Generic;
namespace CommandPattern
{
public interface ICommand
{
void Execute();
}
/* The Invoker class */
public class Switch
{
private List<ICommand> _commands = new List<ICommand>();
public void StoreAndExecute(ICommand command)
{
_commands.Add(command);
command.Execute();
}
}
/* The Receiver class */
public class Light
{
public void TurnOn()
{
Console.WriteLine("The light is on");
}
public void TurnOff()
{
Console.WriteLine("The light is off");
}
}
/* The Command for turning on the light - ConcreteCommand #1 */
public class FlipUpCommand : ICommand
{
private Light _light;
public FlipUpCommand(Light light)
{
_light = light;
}
public void Execute()
{
_light.TurnOn();
}
}
/* The Command for turning off the light - ConcreteCommand #2 */
public class FlipDownCommand : ICommand
{
private Light _light;
public FlipDownCommand(Light light)
{
_light = light;
}
public void Execute()
{
_light.TurnOff();
}
}
/* The test class or client */
internal class Program
{
public static void Main(string[] args)
{
Light lamp = new Light();
ICommand switchUp = new FlipUpCommand(lamp);
ICommand switchDown = new FlipDownCommand(lamp);
Switch s = new Switch();
string arg = args.Length > 0 ? args[0].ToUpper() : null;
if (arg == "ON")
{
s.StoreAndExecute(switchUp);
}
else if (arg == "OFF")
{
s.StoreAndExecute(switchDown);
}
else
{
Console.WriteLine("Argument \"ON\" or \"OFF\" is required.");
}
}
}
}
以下程式碼是 Python 中命令模式的實現。
class Switch(object):
"""The INVOKER class"""
def __init__(self, flip_up_cmd, flip_down_cmd):
self.flip_up = flip_up_cmd
self.flip_down = flip_down_cmd
class Light(object):
"""The RECEIVER class"""
def turn_on(self):
print "The light is on"
def turn_off(self):
print "The light is off"
class LightSwitch(object):
"""The CLIENT class"""
def __init__(self):
lamp = Light()
self._switch = Switch(lamp.turn_on, lamp.turn_off)
def switch(self, cmd):
cmd = cmd.strip().upper()
if cmd == "ON":
self._switch.flip_up()
elif cmd == "OFF":
self._switch.flip_down()
else:
print 'Argument "ON" or "OFF" is required.'
# Execute if this file is run as a script and not imported as a module
if __name__ == "__main__":
light_switch = LightSwitch()
print "Switch ON test."
light_switch.switch("ON")
print "Switch OFF test."
light_switch.switch("OFF")
print "Invalid Command test."
light_switch.switch("****")
/* The Command interface */
trait Command {
def execute()
}
/* The Invoker class */
class Switch {
private var history: List[Command] = Nil
def storeAndExecute(cmd: Command) {
cmd.execute()
this.history :+= cmd
}
}
/* The Receiver class */
class Light {
def turnOn() = println("The light is on")
def turnOff() = println("The light is off")
}
/* The Command for turning on the light - ConcreteCommand #1 */
class FlipUpCommand(theLight: Light) extends Command {
def execute() = theLight.turnOn()
}
/* The Command for turning off the light - ConcreteCommand #2 */
class FlipDownCommand(theLight: Light) extends Command {
def execute() = theLight.turnOff()
}
/* The test class or client */
object PressSwitch {
def main(args: Array[String]) {
val lamp = new Light()
val switchUp = new FlipUpCommand(lamp)
val switchDown = new FlipDownCommand(lamp)
val s = new Switch()
try {
args(0).toUpperCase match {
case "ON" => s.storeAndExecute(switchUp)
case "OFF" => s.storeAndExecute(switchDown)
case _ => println("Argument \"ON\" or \"OFF\" is required.")
}
} catch {
case e: Exception => println("Arguments required.")
}
}
}
以下程式碼是 Javascript 中命令模式的實現。
/* The Invoker function */
var Switch = function(){
this.storeAndExecute = function(command){
command.execute();
}
}
/* The Receiver function */
var Light = function(){
this.turnOn = function(){ console.log ('turn on')};
this.turnOff = function(){ console.log ('turn off') };
}
/* The Command for turning on the light - ConcreteCommand #1 */
var FlipUpCommand = function(light){
this.execute = light.turnOn;
}
/* The Command for turning off the light - ConcreteCommand #2 */
var FlipDownCommand = function(light){
this.execute = light.turnOff;
}
var light = new Light();
var switchUp = new FlipUpCommand(light);
var switchDown = new FlipDownCommand(light);
var s = new Switch();
s.storeAndExecute(switchUp);
s.storeAndExecute(switchDown);
Object subclass: #Switch
instanceVariableNames:
' flipUpCommand flipDownCommand '
classVariableNames: ''
poolDictionaries: ''
Object subclass: #Light
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
Object subclass: #PressSwitch
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
!Switch class methods !
upMessage: flipUpMessage downMessage: flipDownMessage
^self new upMessage: flipUpMessage downMessage: flipDownMessage; yourself.! !
!Switch methods !
upMessage: flipUpMessage downMessage: flipDownMessage
flipUpCommand := flipUpMessage.
flipDownCommand := flipDownMessage.!
flipDown
flipDownCommand perform.!
flipUp
flipUpCommand perform.! !
!Light methods !
turnOff
Transcript show: 'The light is off'; cr.!
turnOn
Transcript show: 'The light is on'; cr.! !
!PressSwitch class methods !
switch: state
" This is the test method "
| lamp switchUp switchDown switch |
lamp := Light new.
switchUp := Message receiver: lamp selector: #turnOn.
switchDown := Message receiver: lamp selector: #turnOff.
switch := Switch upMessage: switchUp downMessage: switchDown.
state = #on ifTrue: [ ^switch flipUp ].
state = #off ifTrue: [ ^switch flipDown ].
Transcript show: 'Argument #on or #off is required.'.
! !
以下程式碼是 PHP 中命令模式的實現。
<?php
interface Command
{
public function execute();
}
/** The Invoker class */
class Switcher
{
private $history = array();
public function storeAndExecute(Command $cmd)
{
$this->history[] = $cmd;
$cmd->execute();
}
}
/** The Receiver class */
class Light
{
public function turnOn()
{
echo("The light is on");
}
public function turnOff()
{
echo("The light is off");
}
}
/** The Command for turning on the light - ConcreteCommand #1 */
class FlipUpCommand implements Command {
private $theLight;
public function __construct(Light $light) {
$this->theLight = $light;
}
public function execute() {
$this->theLight->turnOn();
}
}
/** The Command for turning off the light - ConcreteCommand #2 */
class FlipDownCommand implements Command {
private $theLight;
public function __construct(Light $light) {
$this->theLight = $light;
}
public function execute() {
$this->theLight->turnOff();
}
}
/* The test class or client */
class PressSwitch {
public static function main(string $args){
$lamp = new Light();
$switchUp = new FlipUpCommand($lamp);
$switchDown = new FlipDownCommand($lamp);
$mySwitch = new Switcher();
switch($args) {
case 'ON':
$mySwitch->storeAndExecute($switchUp);
break;
case 'OFF':
$mySwitch->storeAndExecute($switchDown);
break;
default:
echo("Argument \"ON\" or \"OFF\" is required.");
break;
}
}
}
/*INPUT*/
PressSwitch::main('ON'). PHP_EOL;
PressSwitch::main('OFF'). PHP_EOL;
/*OUTPUT*/
//The light is on
//The light is off