原理或定義
命令模式將一個請求封裝爲一個對象,從而使你可用不同的請求對客戶進行參數化;對請求排隊或記錄請求日誌,以及支持可撤銷的操作。
命令模式的本質是對命令進行封裝,將發出命令的責任和執行命令的責任分割開,實現二者之間的松耦合。
結構
Command: 定義命令的接口,聲明執行的方法。
ConcreteCommand: 命令接口實現對象,通常會持有接受者,並調用接受者的功能來完成命令要執行的操作。
Receiver: 接收者,真正執行命令的對象。任何類都可能成爲一個接收者,只要它能夠實現命令要求的相應功能。
Invoker: 要求命令對象執行請求,通常會持有命令對象,可以持有很多的命令對象。這個是客戶端真正觸發命令並要求命令執行相應操作的地方,相當於使用命令對象的入口。
Client: 創建具體的命令對象,並且設置命令對象的接收者。
類圖
案例與代碼
本案例使用家電自動化遙控器API項目來作爲示例
通過一個遙控器同時控制一個或多個電燈和音響。
一般設計方案:
public interface Control {
public void onButton(int slot);
public void offButton(int slot);
public void undoButton();
}
public class TraditionControl implements Control {
Light light;
Stereo stereo;
public TraditionControl(Light light, Stereo stereo) {
this.light = light;
this.stereo = stereo;
}
@Override
public void onButton(int slot) {
// TODO Auto-generated method stub
switch (slot) {
case 0:
light.On();
break;
case 1:
stereo.On();
break;
case 2:
int vol = stereo.GetVol();
if (vol < 11) {
stereo.SetVol(++vol);
}
break;
}
}
@Override
public void offButton(int slot) {
// TODO Auto-generated method stub
switch (slot) {
case 0:
light.Off();
break;
case 1:
stereo.Off();
break;
case 2:
int vol = stereo.GetVol();
if (vol > 0) {
stereo.SetVol(--vol);
}
break;
}
}
@Override
public void undoButton() {
// TODO Auto-generated method stub
}
}
public class ControlTest {
public static void main(String[] args) {
Control ctl;
Light light = new Light("Bedroom");
Stereo stereo = new Stereo();
ctl = new TraditionControl(light, stereo);
ctl.onButton(0);
ctl.offButton(0);
ctl.onButton(1);
ctl.onButton(2);
ctl.offButton(2);
ctl.offButton(1);
}
}
每次擴展設備和添加按鈕都會修改以上的類。
命令模式設計方案:
類圖:
Command接口:
public interface Command {
public void execute();
public void undo();
}
ConcreteCommand 實現類:
public class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light)
{
this.light=light;
}
@Override
public void execute() {
// TODO Auto-generated method stub
light.Off();
}
@Override
public void undo() {
// TODO Auto-generated method stub
light.On();
}
}
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light)
{
this.light=light;
}
@Override
public void execute() {
// TODO Auto-generated method stub
light.On();
}
@Override
public void undo() {
// TODO Auto-generated method stub
light.Off();
}
}
public class StereoAddVolCommand implements Command {
private Stereo setreo;
public StereoAddVolCommand(Stereo setreo)
{
this.setreo=setreo;
}
@Override
public void execute() {
// TODO Auto-generated method stub
int vol= setreo.GetVol();
if(vol<11)
{
setreo.SetVol(++vol);
}
}
@Override
public void undo() {
// TODO Auto-generated method stub
int vol= setreo.GetVol();
if(vol>0)
{
setreo.SetVol(--vol);
}
}
}
public class StereoOffCommand implements Command {
private Stereo setreo;
public StereoOffCommand(Stereo setreo)
{
this.setreo=setreo;
}
@Override
public void execute() {
// TODO Auto-generated method stub
setreo.Off();
}
@Override
public void undo() {
// TODO Auto-generated method stub
setreo.On();
setreo.SetCd();
}
}
public class StereoOnCommand implements Command {
private Stereo setreo;
public StereoOnCommand(Stereo setreo)
{
this.setreo=setreo;
}
@Override
public void execute() {
// TODO Auto-generated method stub
setreo.On();
setreo.SetCd();
}
@Override
public void undo() {
// TODO Auto-generated method stub
setreo.Off();
}
}
public class StereoSubVolCommand implements Command {
private Stereo setreo;
public StereoSubVolCommand(Stereo setreo)
{
this.setreo=setreo;
}
@Override
public void execute() {
// TODO Auto-generated method stub
int vol= setreo.GetVol();
if(vol>0)
{
setreo.SetVol(--vol);
}
}
@Override
public void undo() {
// TODO Auto-generated method stub
int vol= setreo.GetVol();
if(vol<11)
{
setreo.SetVol(++vol);
}
}
}
public class NoCommand implements Command {
@Override
public void execute() {
// TODO Auto-generated method stub
}
@Override
public void undo() {
// TODO Auto-generated method stub
}
}
Receiver(設備類):
public class Light {
String loc = "";
public Light(String loc) {
this.loc = loc;
}
public void On() {
System.out.println(loc + " On");
}
public void Off() {
System.out.println(loc + " Off");
}
}
public class Stereo {
static int volume = 0;
public void On() {
System.out.println("Stereo On");
}
public void Off() {
System.out.println("Stereo Off");
}
public void SetCd() {
System.out.println("Stereo SetCd");
}
public void SetVol(int vol) {
volume = vol;
System.out.println("Stereo volume=" + volume);
}
public int GetVol() {
return volume;
}
public void Start() {
System.out.println("Stereo Start");
}
}
Invoker:
public class CommandModeControl implements Control{
private Command[] onCommands;
private Command[] offCommands;
private Stack<Command> stack=new Stack<Command>();
public CommandModeControl()
{
onCommands=new Command[5];
offCommands=new Command[5];
Command noCommand=new NoCommand();
for(int i=0,len=onCommands.length;i<len;i++)
{
onCommands[i]=noCommand;
offCommands[i]=noCommand;
}
}
public void setCommand(int slot,Command onCommand,Command offCommand)
{
onCommands[slot]=onCommand;
offCommands[slot]=offCommand;
}
@Override
public void onButton(int slot) {
onCommands[slot].execute();
stack.push(onCommands[slot]);
}
@Override
public void offButton(int slot) {
offCommands[slot].execute();
stack.push(offCommands[slot]);
}
@Override
public void undoButton() {
// TODO Auto-generated method stub
stack.pop().undo();
}
}
Client:
public class ControlTest {
public static void main(String[] args) {
CommandModeControl control = new CommandModeControl();
MarcoCommand onmarco,offmarco;
Light bedroomlight = new Light("BedRoom");
Light kitchlight = new Light("Kitch");
Stereo stereo = new Stereo();
LightOnCommand bedroomlighton = new LightOnCommand(bedroomlight);
LightOffCommand bedroomlightoff = new LightOffCommand(bedroomlight);
LightOnCommand kitchlighton = new LightOnCommand(kitchlight);
LightOffCommand kitchlightoff = new LightOffCommand(kitchlight);
Command[] oncommands={bedroomlighton,kitchlighton};
Command[] offcommands={bedroomlightoff,kitchlightoff};
onmarco=new MarcoCommand(oncommands);
offmarco=new MarcoCommand(offcommands);
StereoOnCommand stereoOn = new StereoOnCommand(stereo);
StereoOffCommand stereoOff = new StereoOffCommand(stereo);
StereoAddVolCommand stereoaddvol = new StereoAddVolCommand(stereo);
StereoSubVolCommand stereosubvol = new StereoSubVolCommand(stereo);
control.setCommand(0, bedroomlighton, bedroomlightoff);
control.setCommand(1, kitchlighton, kitchlightoff);
control.setCommand(2, stereoOn, stereoOff);
control.setCommand(3, stereoaddvol, stereosubvol);
control.setCommand(4, onmarco, offmarco);
control.onButton(0);
control.undoButton();
//control.offButton(0);
control.onButton(1);
control.offButton(1);
control.onButton(2);
control.onButton(3);
control.offButton(3);
control.undoButton();
control.offButton(2);
control.undoButton();
control.onButton(4);
control.offButton(4);
}
}
使用場景
1.系統需要將請求調用者和請求接收者解耦,使得調用者和接收者不直接交互。
2.系統需要在不同的時間指定請求,將請求排隊和執行請求。
3.系統需要支持命令的撤銷和恢復操作。
4.系統需要將一組操作組合在一起,即支持宏命令
優缺點
主要優點有:
1.降低系統的耦合度
2.新的命令可以很容易地加入到系統中
3.可以比較容易地設計一個組合命令
缺點主要有:
使用命令模式可能會導致某些系統有過多的具體命令類。因爲針對每一個命令都需要設計一個具體命令類,因此某些系統可能需要大量具體命令類,這將影響命令模式的使用。