天天看點

Java設計模式筆記之指令模式

概念

指令模式将“請求”封裝成對象,這可以讓你使用不同的請求、隊列、或者日志請求來參數化其他對象。指令模式也支援撤銷操作。

有時候在程式中需要在一個對象中處理很多個請求,這些請求對象沒有實作統一的接口,需要寫很多的判斷語句加以區分不同的請求,然後執行相應的操作。指令模式提供了一種新的思路,通過建立一個新的對象指令對象,定義統一的方法名稱,并且指令對象中有一個請求對象屬性(組合)封裝請求對象,在代碼中調用指令對象統一的方法操作請求對象,不用寫if語句區分不同的請求對象。

類圖

Java設計模式筆記之指令模式

指令模式将請求封裝在一個對象中,允許代碼像操作對象一樣去操作不同的方法,傳遞并且在合适的時機去調用請求對象的方法。有時候需要執行一個請求,但又不知道具體是什麼請求,指令模式可以通過指令對象封裝請求,把指令對象當作參數傳遞,執行指令對象中的方法。這樣代碼中操作的就是統一的指令對象,就可以把代碼規範化。

代碼例子

在這我們以《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、增加一個具體的指令對象很容易。