設計模式的藝術 行為型模式之命令模式
前言
裝修新房子的最後幾道工序之一是安裝插座和開關,通過開關可以控制一些電器的開啟和關閉,例如電燈或者排氣扇,但是購買開關時其實並不知道它能夠控制什麼具體的電器。在軟體設計中,也存在著這樣的關係,請求傳送者和接收者物件,為了降低之間的耦合度,我們會將兩者進行解耦,會在兩者之間引入了新的命令物件,我們將它成為命令模式
什麼是命令模式 Command Pattern
將一個請求封裝為一個物件,從而可用不同的請求對客戶端進行引數化;對請求排隊或者記錄請求日誌,以及支援可撤銷操作。命令模式是一種物件行為型模式,其別名為動作模式或者事務模式
命名模式的優點
(1)、降低了系統的耦合度。由於請求者與接收者之間不存在直接引用,因此請求者與接收者直接實現了完全的解耦,相同的請求者可以對應不同的接收者,同樣,相同的接收者也可以供不同的請求者使用,兩者之間具有良好的獨立性。
(2)、新的命令可以很容易的加入到系統當中,由於增加新的具體命令類不會影響到其他類,因此增加新的具體命令類很容易,無須修改原有系統原始碼甚至客戶類程式碼,滿足開閉原則的要求
(3)、可以比較容易的設計一個命令佇列或巨集命令(組合命令)
(4)、為請求的撤銷和恢復操作提供了一種設計和實現的方案
命令模式的缺點
使用命令模式可能會導致某些系統有過多的具體命令類。因為針對每一個對請求接收者的呼叫操作都需要設計一個具體的命令類,因此在某些系統中可能需要提供大量的具體命令類,這將影響命令模式的使用。
命令模式的使用場景
(1)、系統中需要將請求呼叫者和請求接收者進行解耦,使得呼叫者和接收者不直接互動。請求呼叫者無須知道接收者的存在,無須知道接收者是誰,接收者也無須知道何時被呼叫。
(2)、系統需要在不同的時間指定請求,將請求排隊和執行請求。一個命令物件和請求的初始呼叫者可以有不同的生命期,換言之,最初的請求發出者可能已經不在了,而命令物件本身依舊是活動的,可以通過該命令物件去呼叫請求接收者,無須關心請求呼叫者的存在性,可以通過請求日誌檔案等機制來具體實現。
(3)、系統需要支援命令的撤銷和恢復操作
(4)、系統需要將一組操作組合在一起形成巨集命令。
命令模式的具體實現
目錄結構
請求接收者類
//幫助文件處理類:請求接收者 public class HelpHandler { public void display(){ System.out.println("顯示幫助文件!"); } }
抽象命令類
package com.company.command; //抽象命令類 public abstract class Command { public abstract void execute(); }
具體命令類
import com.company.handler.HelpHandler; public class HelpCommand extends Command { private HelpHandler hhobj; //維持對請求接收者的引用 public HelpCommand() { this.hhobj = new HelpHandler(); } @Override //命令執行方法,將呼叫請求接收者的業務方法 public void execute() { hhobj.display(); } }
//最小化命令類:具體命令類 public class MinimizeCommand extends Command { private WindowHandler whobj; //維持對請求接收者的引用 public MinimizeCommand(){ whobj=new WindowHandler(); } @Override //命令執行方法,將呼叫請求接收者的業務方法 public void execute() { whobj.minimize(); } }
功能鍵類
//功能鍵類:請求傳送者 public class FunctionButton { private String name; //功能鍵名稱 private Command command; //維持一個抽象命令物件的引用 public String getName() { return name; } public FunctionButton(String name) { this.name = name; } //為功能鍵注入命令 public void setCommand(Command command) { this.command = command; } //傳送請求的方法 public void onClick(){ System.out.println("點選功能鍵"); command.execute(); } }
功能鍵設定視窗類
//功能鍵設定視窗類 public class FBSettingWindow { private String title; // 視窗標題 //定義一個ArrayList來儲存所有功能鍵 private ArrayList<FunctionButton> functionButtons=new ArrayList<FunctionButton>(); public FBSettingWindow(String title) { this.title = title; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public void addFunctionButton(FunctionButton fb){ functionButtons.add(fb); } public void removeFunctionButton(FunctionButton fb){ functionButtons.remove(fb); } //顯示視窗及功能 public void dispay(){ System.out.println("顯示視窗:"+this.title); System.out.println("顯示功能鍵:"); for(Object obj:functionButtons){ System.out.println(((FunctionButton)obj).getName()); } System.out.println("-----------------------------"); } }
輔助功能類
//輔助工具類 public class XMLUtil { public static Object getBean(int i){ try { //建立文件的物件 DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance(); DocumentBuilder builder=factory.newDocumentBuilder(); Document document=builder.parse(new File(XMLUtil.class.getClassLoader().getResource("").getPath()+"config.xml")); //獲取包含類名的文字節點 NodeList nodeList=document.getElementsByTagName("className"); Node node=null; if(0==i){ node=nodeList.item(0).getFirstChild(); }else{ node=nodeList.item(1).getFirstChild(); } String cName=node.getNodeValue(); //通過類名生成例項物件並將其返回 Class c=Class.forName(cName); Object object=c.newInstance(); return object; }catch (Exception e){ e.printStackTrace(); return null; } } }
客戶端測試類
public class Client { public static void main(String[] args) { FBSettingWindow fbSettingWindow=new FBSettingWindow("功能鍵設定"); FunctionButton fb1,fb2; fb1=new FunctionButton("功能鍵1"); fb1=new FunctionButton("功能鍵1"); fb2=new FunctionButton("功能鍵2"); Command command1,command2; //通過配置檔案和反射生成具體命令物件 command1=(Command)XMLUtil.getBean(0); command2=(Command)XMLUtil.getBean(1); //將命令物件注入功能鍵 fb1.setCommand(command1); fb2.setCommand(command2); fbSettingWindow.addFunctionButton(fb1); fbSettingWindow.addFunctionButton(fb2); fbSettingWindow.dispay(); //呼叫功能鍵的業務方法 fb1.onClick(); fb2.onClick(); } }