設計模式——命令模式
軟體開發中,通常會存在 “方法的請求者” 與 “方法的實現者” 之間存在緊密的耦合關係。這不利於軟體功能的擴充套件與維護。特別是針對行為進行(撤銷、重做、記錄)一系列操作時很不方便,因此 “如何將方法的請求者與方法的實現者解耦”,是命令模式的主要任務和功能。在現實生活中,這樣的例子也很多,例如,電視機遙控器(命令傳送者)通過按鈕(具體命令)來遙控電視機(命令接收者)
一、命令模式的基本介紹
1)、命令模式(Command Pattern):是一種資料驅動的設計模式,它屬於行為型模式。請求以命令的形式包裹在物件中,並傳遞給物件。呼叫物件尋找可以處理該命令的合適的物件,並把該命令傳給相應的物件,該物件執行命令。
2)、命令模式使得請求傳送者與請求接受者消除彼此之間的耦合,讓物件之間的呼叫關係更加靈活,實現解耦。
3)、在命令模式中,會將一個請求封裝為一個物件,以便使用不同的引數(執行者)來表示不同的請求。同時命令模式也支援撤銷的操作。
4)、增加或刪除命令非常方便。採用命令模式增加和刪除命令不會影響其他類,它滿足 “開閉原則” ,即擴充套件靈活。
5)、可以實現巨集命令。命令模式可以與組合模式結合,將多個命令裝配成一個組合命令,即巨集命令。
6)、方便實現 Undo 和 Redo 操作(適合命令模式)。命令模式可以與後面介紹的備忘錄模式結合,實現命令的撤銷與恢復。
7)、其缺點是:可能產生大量具體命令類。因為對每一個具體操作都需要設計一個具體命令類,這將增加系統的複雜性。
二、命令模式結構類圖
命令模式包含以下主要角色:
【1】、介面命令(Command)角色:宣告執行命令的介面,擁有執行命令的抽象方法。
【2】、具體命令(Concrete Command)角色:是抽象命令類的具體實現類,它擁有接收者物件,並通過呼叫接收者的功能來完成命令要執行的操作。
【3】、接收者(Receiver)角色:執行命令功能的相關操作,是具體命令物件業務的真正實現者。
【4】、呼叫者(Invoker)角色:是請求的傳送者,它通常擁有很多的命令物件,並通過訪問命令物件來執行相關請求,不直接訪問接收者。
三、命令模式案例分析
我們通過寫一個空調遙控器按鈕的案例來體會命令模式的特點:
【1】介面命令角色
1 public interface Command { 2 //命令的執行方法 3 public void execute(); 4 //撤銷操作 5 public void undo(); 6 }
【2】具體命令實現類:寫一個制熱的命令類,實現命令介面,並組合接受者角色,呼叫目標方法。類似的類還有製冷等等。
1 //制熱命令 2 public class HeadCommand implements Command{ 3 //組合空調具體執行類 4 privateAirCondition airCondition; 5 //構造器 6 public HeadCommand(AirCondition airCondition) { 7 super(); 8 this.airCondition = airCondition; 9 } 10 11 @Override 12 public void execute() { 13 //呼叫空調的制熱方法 14 airCondition.Head(); 15 } 16 17 @Override 18 public void undo() { 19 //返回上一次操作 20 airCondition.refrigeration(); 21 } 22 23 }
【3】接收者角色:空調類(AirCondition )
1 //空調類 2 public class AirCondition { 3 //制熱 4 public void Head() { 5 System.out.println("空調製熱......."); 6 } 7 //製冷 8 public void refrigeration() { 9 System.out.println("空調開始製冷......"); 10 } 11 }
【4】呼叫者角色:遙控器類 (RemoteController )
1 //呼叫者( 遙控器 ),也是命令模式的精華 2 public class RemoteController { 3 //新增命令按鈕 4 Command[] commands; 5 //撤銷按鈕 6 Command undo; 7 //構造器 8 public RemoteController() { 9 //初始化按鈕 10 commands = new Command[5]; 11 for(int i=0;i<5;i++) { 12 commands[i] = new NoCommand(); 13 } 14 } 15 16 //給遙控器新增按鈕 17 public void setCommand(int n , Command command) { 18 commands[n]=command; 19 } 20 21 //呼叫制熱按鈕 22 public void headCommonButton(int n) { 23 commands[n].execute(); 24 } 25 26 //撤回 27 public void undoButton() { 28 undo.undo(); 29 } 30 31 }
【5】客戶端呼叫
1 public class Client { 2 public static void main(String[] args) { 3 //建立空調例項 4 AirCondition airCondition = new AirCondition(); 5 //呼叫命令類 6 RemoteController remoteController = new RemoteController(); 7 //將命令新增至遙控按鈕中 8 HeadCommand headCommand = new HeadCommand(airCondition); 9 remoteController.setCommand(0,headCommand); 10 //呼叫制熱功能 11 remoteController.headCommonButton(0); 12 } 13 }
【注意】命令模式的好處:當增加新產品時,只需要建立新產品類即可。無需修改命令類,符合開閉原則。例如我們增加一個冰箱的制熱功能。只需要新增冰箱實體類和制熱命令類,同時在客戶端將其新增至命令類中即可,無需修改命令類。