概念
指令模式将“請求”封裝成對象,這可以讓你使用不同的請求、隊列、或者日志請求來參數化其他對象。指令模式也支援撤銷操作。
有時候在程式中需要在一個對象中處理很多個請求,這些請求對象沒有實作統一的接口,需要寫很多的判斷語句加以區分不同的請求,然後執行相應的操作。指令模式提供了一種新的思路,通過建立一個新的對象指令對象,定義統一的方法名稱,并且指令對象中有一個請求對象屬性(組合)封裝請求對象,在代碼中調用指令對象統一的方法操作請求對象,不用寫if語句區分不同的請求對象。
類圖
指令模式将請求封裝在一個對象中,允許代碼像操作對象一樣去操作不同的方法,傳遞并且在合适的時機去調用請求對象的方法。有時候需要執行一個請求,但又不知道具體是什麼請求,指令模式可以通過指令對象封裝請求,把指令對象當作參數傳遞,執行指令對象中的方法。這樣代碼中操作的就是統一的指令對象,就可以把代碼規範化。
代碼例子
在這我們以《headfirst設計模式》中的例子進行說明。有個遙控器有7個插槽,用來控制不同的電器,每個插槽對應兩個按鈕,開和關。遙控器有一個總的撤銷按鈕。如果遙控器的插槽有裝置插入,按對應的開、關和撤銷按鈕就可以控制電器。下面是具體的代碼實作。
package cn.lzz.hf.command;
/**
* 指令對象接口
* @author Administrator
*
*/
public interface Command {
/**
* 執行請求
*/
void execute();
/**
* 撤銷請求
*/
void undo();
}
package cn.lzz.hf.command;
/**
* 電燈對象
* @author Administrator
*
*/
public class Light {
public void on(){
System.out.println("Light is on.");
}
public void off(){
System.out.println("Light is off.");
}
}
package cn.lzz.hf.command;
/**
* 關燈指令對象
* @author Administrator
*
*/
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();
}
}
package cn.lzz.hf.command;
/**
* 開燈指令對象
* @author Administrator
*
*/
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();
}
}
package cn.lzz.hf.command;
/**
* 收音機對象
* @author Administrator
*
*/
public class Stereo {
public void on(){
System.out.println("Stereo is on");
}
public void off(){
System.out.println("Stereo is off");
}
}
package cn.lzz.hf.command;
/**
* 關收音機指令對象
* @author Administrator
*
*/
public class StereoOffCommand implements Command {
private Stereo stereo;
public StereoOffCommand(Stereo stereo){
this.stereo=stereo;
}
@Override
public void execute() {
// TODO Auto-generated method stub
stereo.off();
}
@Override
public void undo() {
// TODO Auto-generated method stub
stereo.on();
}
}
package cn.lzz.hf.command;
/**
* 開收音機指令對象
* @author Administrator
*
*/
public class StereoOnCommand implements Command {
private Stereo stereo;
public StereoOnCommand(Stereo stereo){
this.stereo=stereo;
}
@Override
public void execute() {
// TODO Auto-generated method stub
stereo.on();
}
@Override
public void undo() {
// TODO Auto-generated method stub
stereo.off();
}
}
package cn.lzz.hf.command;
/**
* 空指令對象
* @author Administrator
*
*/
public class NoCommand implements Command {
@Override
public void execute() {
// TODO Auto-generated method stub
}
@Override
public void undo() {
// TODO Auto-generated method stub
}
}
package cn.lzz.hf.command;
import java.util.Stack;
/**
* 遙控器對象
* @author Administrator
*
*/
public class RemoteControl {
//打開按鈕數組
private Command[] onCommands;
//關閉按鈕數組
private Command[] offCommands;
//撤銷指令棧
private Stack<Command> undoCommandStack;
/**
* 初始化
*/
public RemoteControl(){
onCommands=new Command[7];
offCommands=new Command[7];
Command tempCommand=new NoCommand();
for(int i=0;i<7;i++){
onCommands[i]=tempCommand;
offCommands[i]=tempCommand;
}
undoCommandStack=new Stack<Command>();
}
/**
* 設定指令對象
* @param soltIndex
* @param onCommand
* @param offCommand
*/
public void setCommand(int soltIndex,Command onCommand,Command offCommand){
onCommands[soltIndex]=onCommand;
offCommands[soltIndex]=offCommand;
}
/**
* on
* @param soltIndex
*/
public void onButtonWasPressed(int soltIndex){
onCommands[soltIndex].execute();
//記錄指令對象
undoCommandStack.push(onCommands[soltIndex]);
}
/**
* off
* @param soltIndex
*/
public void offButtonWasPressed(int soltIndex){
offCommands[soltIndex].execute();
//記錄指令對象
undoCommandStack.push(offCommands[soltIndex]);
}
/**
* 撤銷方法
*/
public void undoButtonWasPressed(){
while(!undoCommandStack.isEmpty()){
undoCommandStack.pop().undo();
}
}
@Override
public String toString() {
StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append("\n-------------- Remote Control ----------------\n");
for(int i=0;i<onCommands.length;i++){
stringBuilder.append("[solt "+i+"]"+onCommands[i].getClass().getName()
+" "+offCommands[i].getClass().getName()+"\n");
}
return stringBuilder.toString();
}
}
package cn.lzz.hf.command;
/**
* 測試代碼
* @author Administrator
*
*/
public class RemoteControlTest {
public static void main(String[] args) {
//遙控器對象
RemoteControl control=new RemoteControl();
//電燈對象
Light light=new Light();
Command onLight=new LightOnCommand(light);
Command offLight=new LightOffCommand(light);
control.setCommand(0, onLight, offLight);
//收音機對象
Stereo stereo=new Stereo();
Command onStereo=new StereoOnCommand(stereo);
Command offStereo=new StereoOffCommand(stereo);
control.setCommand(1, onStereo, offStereo);
System.out.println("開燈-》關燈-》撤銷");
control.onButtonWasPressed(0);
control.offButtonWasPressed(0);
control.undoButtonWasPressed();
System.out.println("關燈-》開燈-》撤銷");
control.offButtonWasPressed(0);
control.onButtonWasPressed(0);
control.undoButtonWasPressed();
System.out.println("關閉收音機-》打開收音機-》撤銷");
control.offButtonWasPressed(1);
control.onButtonWasPressed(1);
control.undoButtonWasPressed();
System.out.println("打開收音機-》關閉收音機-》撤銷");
control.onButtonWasPressed(1);
control.offButtonWasPressed(1);
control.undoButtonWasPressed();
System.out.println("關燈-》打開收音機-》撤銷");
control.offButtonWasPressed(0);
control.onButtonWasPressed(1);
control.undoButtonWasPressed();
}
}
執行結果
開燈-》關燈-》撤銷
Light is on.
Light is off.
Light is on.
Light is off.
關燈-》開燈-》撤銷
Light is off.
Light is on.
Light is off.
Light is on.
關閉收音機-》打開收音機-》撤銷
Stereo is off
Stereo is on
Stereo is off
Stereo is on
打開收音機-》關閉收音機-》撤銷
Stereo is on
Stereo is off
Stereo is on
Stereo is off
關燈-》打開收音機-》撤銷
Light is off.
Stereo is on
Stereo is off
Light is on.
上面代碼中,我們通過指令對象Command封裝了Light和Stereo對象的開和關指令,在遙控器中可以調用參數對象Command,利用多态的特性模闆化遙控器的代碼,相容不同的電器對象。如果需要增加新的電器,隻需要建立相應的Command對象就可以實作,不用更改遙控器中的代碼。
遙控器代碼初始化時, 7個插槽設定了相同的空指令對象,這樣在開、關按鈕被按下的時候,不用判斷插槽中是否有裝置,也就是代碼中不用判空。這是開發中常用的一中技巧。
總結
指令模式有以下幾個優點
1、指令模式通過指令對象把操作對象和請求對象解耦;
2、請求對象可以利用多态的特性複用具體的指令對象;
3、一個指令對象可以執行多個請求對象的具體操作;
4、增加一個具體的指令對象很容易。