第34章 命令模式+責任鏈模式
34.1 搬移UNIX的命令
在操作系統的世界里,有兩大陣營一直在PK著:*nix(包括UNIX和Linux)和Windows。從目前的統計數據來看,*nix在應用服務器領域占據相對優勢,不過Windows也不甘示弱,國內某些小型銀行已經在使用PC Server(安裝Windows操作系統的服務器)集群來進行銀行業務運算,而且穩定性、性能各方面的效果不錯;而在個人桌面方面,Windows是占絕對優勢的,大家應該基本上都在用這個操作系統,它的諸多優點這里就不多說了,我們今天就來解決一個習慣問題。如果你負責過UNIX系統維護,你自己的筆記本又是Windows操作系統的話,我想你肯定有這樣的經驗,如圖34-1所示。

圖34-1 時常犯的錯誤
是不是經常把UNIX上的命令敲到Windows系統了?為了避免這種情況發生,可以把UNIX上的命令移植到Windows上,也就是Windows下的shell工具,有很多類似的工具,比如cygwin、GUN Bash等,這些都是非常完美的工具,我們今天的任務就是自己寫一個這樣的工具。怎么寫呢?我們學了這么多的模式,當然要融會貫通了,可以使用命令模式、責任鏈模式、模板方法模式設計一個方便擴展、穩定的工具。
我們先說說UNIX下的命令,一條命令分為命令名、選項和操作數,例如命令"ls-l/usr",其中,ls是命令名,l是選項,/usr是操作數,后兩項都是可選項,根據實際情況而定。UNIX命令一定遵守以下幾個規則:
● 命令名為小寫字母。
● 命令名、選項、操作數之間以空格分隔,空格數量不受限制。
● 選項之間可以組合使用,也可以單獨拆分使用。
● 選項以橫杠(-)開頭。
在UNIX世界中,我們最常用的就是ls這個命令,它用于顯示目錄或文件信息,下面我們先來看看這個命令。常用的有以下幾條組合命令:
● ls:簡單列出一個目錄下的文件。
● ls-l:詳細列出目錄下的文件。
● ls-a:列出目錄下包含的隱藏文件,主要是點號(.)開頭的文件。
● ls-s:列出文件的大小。
除此之外,還有一些非常常用的組合命令,如"ls-la"、"ls-ls"等。ls命令名確定了,但是其后連接的選項和操作數是不確定的。操作數我們不用關心它,每個命令必然有一個操作數,若沒有則是當前的目錄。問題的關鍵是選項,用哪個選項以及什么時候使用都是由用戶決定的,也就是從設計上考慮。設計者需要完全解析所有的參數,需要很多個類來處理如此多的選項,客戶輸入一個參數,立刻返回一個結果。針對一個ls命令族,要求如下:
● 每一個ls命令都有操作數,默認操作數為當前目錄。
● 選項不可重復,例如對于"ls-l-l-s",解析出的選項應該只有兩個:l選項和s選項。
● 每個選項返回不同的結果,也就是說每個選項應該由不同的業務邏輯來處理。
● 為提高擴展性,ls命令族內的運算應該是對外封閉的,減少外界訪問ls命令族內部細節的可能性。
針對一個命令族的分析結果,我們可以使用什么模式?責任鏈模式!對,只要把一個參數傳遞到鏈首,就可以立刻獲得一個結果,中間是如何傳遞的以及由哪個邏輯解析都不需要外界(高層)模塊關心,該模塊的類圖如圖34-2所示。

圖34-2 命令族的解析類圖
類圖還是比較清晰的,UNIX的命令有上百個,我們定義一個CommandName抽象類,所有的命令都繼承于該類,它就是責任鏈模式的handler類,負責鏈表控制;每個命令族都有一個獨立的抽象類,因為每個命令族都有其獨特的個性,比如ls命令和df命令,其后可加的參數是不一樣的,這就可以在抽象類AbstractLS中定義,而且它還有標示作用,標示其下的實現類都是實現ls命令的,只是命令的選項不同;Context負責建立一條命令的鏈表,比如ls命令族、df命令族等,它組裝出一個處理一個命令族的責任鏈,并返回首節點供高層模塊調用,這是非常典型的責任鏈模式。
分析完畢一個具體的命令族,已經確定可以采用責任鏈模式,我們繼續往下分析。UNIX命令非常多,敲一個命令返回一個結果,每個具體的命令可以由相關的命令族(也就是責任鏈)來解析,但是如此多的命令還是需要有一個派發的角色,輸入一個命令,不管后臺誰來解析,返回一個結果就成,這就要用到命令模式。命令模式負責協調各個命令正確地傳遞到各個責任鏈的首節點,這就是它的任務,其類圖如圖34-3所示。

圖34-3 命令傳遞類圖
是不是典型的命令模式類圖?其中Chain是一個標示符,表示的就是我們上面分析的責任鏈,每一個具體的命令負責調用責任鏈的首節點,獲得返回值,結束命令的執行。兩個核心模塊都分析完畢了,就可以把類圖融合在一起,完整的類圖如圖34-4所示。
這個類圖還是比較簡單的,我們來看一下各個類的職責。
● ClassUtils
ClassUtils是工具類,其主要職責是根據一個接口、父類查找到所有的子類。在不考慮效率的應用中,使用該類可以帶來非常好的擴展性。
● CommandVO
CommandVO是命令的值對象,它把一個命令解析為命令名、選項、操作數,例如"ls-l/usr"命令分別解析為getCommandName、getParam、getData三個方法的返回值。
● CommandEnum
CommandEnum是枚舉類型,是主要的命令配置文件。為什么需要枚舉類型?這是JDK 1.5提供的一個非常好的功能,我們在程序中再講解如何使用它。
所有的分析都已經完成了,我們來看看程序。程序不復雜,看看類圖,應該先寫命令的解釋,這是項目的核心。我們先來看CommandName抽象類,如代碼清單34-1所示。

圖34-4 完整類圖
代碼清單34-1 抽象命令名類
public?abstract?class?CommandName?{
?????private?CommandName?nextOperator;
?????public?final?String?handleMessage(CommandVO?vo){
?????????????//處理結果
?????????????String?result?=?"";??????????
?????????????//判斷是否是自己處理的參數
?????????????if(vo.getParam().size()?==?0?||?vo.getParam().contains?(this.getOperateParam())){
?????????????????????result?=?this.echo(vo);
?????????????}else{
?????????????????????if(this.nextOperator?!=null){
?????????????????????????????result?=?this.nextOperator.handleMessage(vo);
?????????????????????}else{
?????????????????????????????result?=?"命令無法執行";
?????????????????????}
?????????????}
?????????????return?result;
?????}
?????//設置剩余參數由誰來處理
?????public?void?setNext(CommandName?_operator){
?????????????this.nextOperator?=?_operator;
?????}
?????//每個處理者都要處理一個后綴參數
?????protected?abstract?String?getOperateParam();
?????//每個處理者都必須實現處理任務
?????protected?abstract?String?echo(CommandVO?vo);
}
很簡單,就是責任鏈模式中的handler,也就是中控程序,控制一個鏈應該如何建立。我們再來看3個ls命令族,先看AbstractLS抽象類,如代碼清單34-2所示。
代碼清單34-2 抽象ls命令
public?abstract?class?AbstractLS?extends?CommandName{
?????//默認參數
?????public?final?static?String?DEFAULT_PARAM?=?"";
?????//參數a
?????public?final?static?String?A_PARAM?="a";
?????//參數l
?????public?final?static?String?L_PARAM?=?"l";
}
很驚訝,是嗎?怎么是個空的抽象類?是的,確實是一個空類,只定義了3個參數名稱,它有兩個職責:
● 標記ls命令族。
● 個性化處理。
因為現在還沒有思考清楚ls有什么個性(可以把命令的選項也認為是其個性化數據),所以先寫個空類放在這里,以后想清楚了再填寫上去,留下一些可擴展的類也許會給未來帶來不可估量的優點。
我們再來看ls不帶任何參數的命令處理,如代碼清單34-3所示。
代碼清單34-3 ls命令
public?class?LS?extends?AbstractLS?{
?????//最簡單的ls命令
?????protected?String?echo(CommandVO?vo)?{
?????????????return?FileManager.ls(vo.formatData());
?????}
?????//參數為空
?????protected?String?getOperateParam()?{
?????????????return?super.DEFAULT_PARAM;
?????}
}
太簡單了,首先定義了自己能處理什么樣的參數,即只能處理不帶參數的ls命令,getOperateParam返回一個長度為零的字符串,就是說該類作為鏈上的一個節點,只處理沒有參數的ls命令。echo方法是執行ls命令,通過調用操作系統相關的命令返回結果。我們再來看ls -l命令,如代碼清單34-4所示。
代碼清單34-4 ls-l命令
public?class?LS_L?extends?AbstractLS?{
?????protected?String?echo(CommandVO?vo)?{
?????????????return?FileManager.ls_l(vo.formatData());
?????}
?????//l選項
?????protected?String?getOperateParam()?{
?????????????return?super.L_PARAM;
?????}
}
該類只處理選項為"l"的命令,也非常簡單。ls-a命令的處理與此類似,如代碼清單34-5所示。
代碼清單34-5 ls-a命令
public?class?LS_A?extends?AbstractLS?{
?????//ls?-a命令
?????protected?String?echo(CommandVO?vo)?{
?????????????return?FileManager.ls_a(vo.formatData());
?????}
?????protected?String?getOperateParam()?{
?????????????return?super.A_PARAM;
?????}
}
這3個實現類都關聯到了FileManager,這個類有什么用呢?它是負責與操作系統交互的。要把UNIX的命令遷移到Windows上運行,就需要調用Windows的低層函數,實現起來較復雜,而且和我們本章要講的內容沒有太大關系,所以這里采用示例性代碼代替,如代碼清單34-6所示。
代碼清單34-6 文件管理類
public?class?FileManager?{
?????//ls命令
?????public?static?String?ls(String?path){
?????????????return?"file1\nfile2\nfile3\nfile4";
?????}
?????//ls?-l命令
?????public?static?String?ls_l(String?path){
?????????????String?str?=?"drw-rw-rw?root?system?1024?2009-8-20?10:23?file1\n";
?????????????str?=?str?+?"drw-rw-rw?root?system?1024?2009-8-20?10:23?file2\n";
?????????????str?=?str?+?"drw-rw-rw?root?system?1024?2009-8-20?10:23?file3";
?????????????return?str;
?????}
?????//ls?-a命令
?????public?static?String?ls_a(String?path){
?????????????String?str?=?".\n..\nfile1\nfile2\nfile3";
?????????????return?str;
?????}
}
以上都是比較簡單的方法,大家有興趣可以自己實現一下,以下提供3種思路:
● 通過java.io.File類自己封裝出類似UNIX的返回格式。
● 通過java.lang.Runtime類的exec方法執行dos的dir命令,產生類似的ls結果。
● 通過JNI(Java Native Interface)來調用與操作系統有關的動態鏈接庫,當然前提是需要自己寫一個動態鏈接庫文件。
3個具體的命令都已經解析完畢,我們再來看看如何建立一條處理鏈,由于建鏈的任務已經移植到抽象命令類,我們就先來看抽象類Command,如代碼清單34-7所示。
代碼清單34-7 抽象命令
public?abstract?class?Command?{
?????public?abstract?String?execute(CommandVO?vo);
?????//建立鏈表
?????protected?final?List<??extends?CommandName>?buildChain(Class<??extends?CommandName>?abstractClass){
?????????????//取出所有的命令名下的子類
?????????????List<Class>?classes?=?ClassUtils.getSonClass(abstractClass);
?????????????//存放命令的實例,并建立鏈表關系
?????????????List<CommandName>?commandNameList?=?new?ArrayList<CommandName>();
?????????????for(Class?c:classes){
?????????????????????CommandName?commandName?=null;
?????????????????????try?{
?????????????????????????????//產生實例
?????????????????????????????commandName?=?(CommandName)Class.forName?(c.getName())?.newInstance();
?????????????????????}?catch?(Exception?e){
?????????????????????????????//?TODO?異常處理
?????????????????????}
?????????????????????//建立鏈表
?????????????????????if(commandNameList.size()>0){
?????????????????????????????commandNameList.get(commandNameList.size()-1).setNext?(commandName);
?????????????????????}
?????????????????????commandNameList.add(commandName);
?????????????}
?????????????return?commandNameList;
?????}
}
Command抽象類有兩個作用:一是定義命令的執行方法,二是負責命令族(責任鏈)的建立。其中buildChain方法負責建立一個責任鏈,它通過接收一個抽象的命令族類就可以建立一條命令解析鏈,如傳遞AbstarctLS類就可以建立一條解析ls命令族的責任鏈,請讀者注意如下這句代碼:
commandName?=?(CommandName)Class.forName(c.getName()).newInstance();
在一個遍歷中,類中的每個元素都是一個類名,然后根據類名產生一個實例,它會拋出異常,例如類文件不存在、初始化失敗等,讀者在設計時要實現該部分的異常。我們再來想一下,每個實現類的類名是如何取得的呢?看下面這句代碼:
List<Class>?classes?=?ClassUtils.getSonClass(abstractClass);
根據一個父類取得所有子類,是一個非常好的工具類,其實現如代碼清單34-8所示。
代碼清單34-8 根據父類獲得子類
public?class?ClassUtils?{
?????//根據父類查找到所有的子類,默認情況是子類和父類都在同一個包名下
?????public?static?List<Class>?getSonClass(Class?fatherClass){
??????????//定義一個返回值
??????????List<Class>?returnClassList?=?new?ArrayList<Class>();
??????????//獲得包名稱
??????????String?packageName?=?fatherClass.getPackage().getName();
??????????//獲得包中的所有類
??????????List<Class>??packClasses?=?getClasses(packageName);
??????????//判斷是否是子類
??????????for(Class?c:packClasses){
???????????????if(fatherClass.isAssignableFrom(c)?&&?!fatherClass.equals(c)){
????????????????????returnClassList.add(c);
???????????????}
??????????}
??????????return?returnClassList;
?????}
?????//從一個包中查找出所有的類,在jar包中不能查找
?????private?static?List<Class>?getClasses(String?packageName)?{
??????????ClassLoader?classLoader?=?Thread.currentThread()
????????????????????.getContextClassLoader();
??????????String?path?=?packageName.replace('.',?'/');
??????????Enumeration<URL>?resources?=?null;
??????????try?{
???????????????resources?=?classLoader.getResources(path);
??????????}?catch?(IOException?e)?{
???????????????//?TODO?Auto-generated?catch?block
???????????????e.printStackTrace();
??????????}
??????????List<File>?dirs?=?new?ArrayList<File>();
??????????while?(resources.hasMoreElements())?{
???????????????URL?resource?=?resources.nextElement();
???????????????dirs.add(new?File(resource.getFile()));
??????????}
??????????ArrayList<Class>?classes?=?new?ArrayList<Class>();
??????????for?(File?directory?:?dirs)?{
???????????????classes.addAll(findClasses(directory,?packageName));
??????????}
??????????return?classes;
?????}
?????private?static?List<Class>?findClasses(File?directory,?String?packageName)?{
????????List<Class>?classes?=?new?ArrayList<Class>();
????????if?(!directory.exists())?{
????????????return?classes;
????????}
????????File[]?files?=?directory.listFiles();
????????for?(File?file?:?files)?{
????????????if?(file.isDirectory())?{
????????????????assert?!file.getName().contains(".");
????????????????classes.addAll(findClasses(file,?packageName?+?"."?+?file.getName()));
????????????}?else?if?(file.getName().endsWith(".class"))?{
????????????????try?{
????????classes.add(Class.forName(packageName?+?'.'?+?file.getName()?.substring(0,?file.getName().length()?-?6)));
????????????????????}?catch?(ClassNotFoundException?e)?{
?????????????????????????e.printStackTrace();
????????????????????}
????????????}
????????}
????????return?classes;
????}
}
這個類請大家謹慎使用,在核心的應用中盡量不要使用該工具,它會嚴重影響性能。
再來看LSCommand類的實現,如代碼清單34-9所示。
代碼清單34-9 具體的ls命令
public?class?LSCommand?extends?Command{
?????public?String?execute(CommandVO?vo){
?????????????//返回鏈表的首節點
?????????????CommandName?firstNode?=?super.buildChain(AbstractLS.class).get(0);
?????????????return?firstNode.handleMessage(vo);
?????}
}
很簡單的方法,先建立一個命令族的責任鏈,然后找到首節點調用。在該類中我們使用CommandVO類,它是一個封裝對象,其代碼如代碼清單34-10所示。
代碼清單34-10 命令對象
public?class?CommandVO?{
?????//定義參數名與參數的分隔符號,一般是空格
?????public?final?static?String?DIVIDE_FLAG?="?";
?????//定義參數前的符號,Unix一般是-,如ls?-la
?????public?final?static?String?PREFIX="-";
?????//命令名,如ls、du
?????private?String?commandName?=?"";
?????//參數列表
?????private?ArrayList<String>?paramList?=?new?ArrayList<String>();
?????//操作數列表
?????private?ArrayList<String>?dataList?=?new?ArrayList<String>();
?????//通過構造函數傳遞進來命令
?????public?CommandVO(String?commandStr){
?????????????//常規判斷
?????????????if(commandStr?!=?null?&&?commandStr.length()?!=0){
?????????????????????//根據分隔符號拆分出執行符號
?????????????????????String[]?complexStr?=?commandStr.split(CommandVO.DIVIDE_FLAG);
?????????????????????//第一個參數是執行符號
?????????????????????this.commandName?=?complexStr[0];
?????????????????????//把參數放到List中
?????????????????????for(int?i=1;i<complexStr.length;i++){
?????????????????????????????String?str?=?complexStr[i];
?????????????????????????????//包含前綴符號,認為是參數
?????????????????????????????if(str.indexOf(CommandVO.PREFIX)==0){
?????????????????????????????this.paramList.add(str.replace
?????????????????????????????(CommandVO.PREFIX,?"").trim());
?????????????????????????????}else{
?????????????????????????????????????this.dataList.add(str.trim());
?????????????????????????????}
?????????????????????}
?????????????}else{
?????????????????????//傳遞的命令錯誤
?????????????????????System.out.println("命令解析失敗,必須傳遞一個命令才能執行!");
?????????????}
?????}
?????//得到命令名
?????public?String?getCommandName(){
?????????????return?this.commandName;
?????}
?????//獲得參數
?????public?ArrayList<String>?getParam(){
?????????????//為了方便處理空參數
?????????????if(this.paramList.size()?==0){
?????????????????????this.paramList.add("");
?????????????}??????????
?????????????return?new?ArrayList(new?HashSet(this.paramList));
?????}
?????//獲得操作數
?????public?ArrayList<String>?getData(){
?????????????return?this.dataList;
?????}
}
CommandVO解析一個命令,規定一個命令必須有3項:命令名、選項、操作數。如果沒有呢?那就以長度為零的字符串代替,通過這樣的一個約定可以大大降低命令解析的開發工作。注意getParam參數中的返回值:
new?ArrayList(new?HashSet(this.paramList));
為什么要這么處理?HashSet具有值唯一的優點,這樣處理就是為了避免出現兩個相同的參數,比如對于"ls-l-l-s"這樣的命令,通過getParam返回的參數是幾個呢?回答是兩個:l選項和s選項。
我們再來看Invoker類,它是負責命令分發的類,如代碼清單34-11所示。
代碼清單34-11 命令分發
public?class?Invoker?{
?????//執行命令
?????public?String??exec(String?_commandStr){
?????????????//定義返回值
?????????????String?result?=?"";
?????????????//首先解析命令
?????????????CommandVO?vo?=?new?CommandVO(_commandStr);
?????????????//檢查是否支持該命令
?????????????if(CommandEnum.getNames().contains(vo.getCommandName())){
??????????????????//產生命令對象
??????????????????String?className?=?CommandEnum.valueOf?(vo.getCommandName())?.getValue();
??????????????????Command?command;
??????????????????try?{
???????????????????????command?=?(Command)Class.forName(className).newInstance();
???????????????????????result?=?command.execute(vo);
??????????????????}catch(Exception?e){
???????????????????????//?TODO?異常處理
??????????????????}???????????????
?????????????}else{
??????????????????result?=?"無法執行命令,請檢查命令格式";
?????????????}
?????????????return?result;
?????}
}
實現也是比較簡單的,從CommandEnum中獲得命令與命令類的配置信息,然后建立一個命令實例,調用其execute方法,完成命令的執行操作。CommandEnum類是一個枚舉類型,如代碼清單34-12所示。
代碼清單34-12 命令配置對象
public?enum?CommandEnum?{
?????ls("com.cbf4life.common.command.LSCommand");
?????private?String?value?=?"";
?????//定義構造函數,目的是Data(value)類型的相匹配
?????private?CommandEnum(String?value){
?????????????this.value?=?value;
?????}
?????public?String?getValue(){
?????????????return?this.value;
?????}
?????//返回所有的enum對象
?????public?static?List<String>?getNames(){
?????????????CommandEnum[]?commandEnum?=?CommandEnum.values();
?????????????List<String>?names?=?new?ArrayList<String>();
?????????????for(CommandEnum?c:commandEnum){
?????????????????????names.add(c.name());
?????????????}
?????????????return?names;
?????}
}
為什么要用枚舉類型?用一個接口來管理也是很容易實現的。注意CommandEnum中的構造函數CommandEnum(String value)和getValue類,沒有新建一個Enum對象,但是可以直接使用CommandEnum.ls.getValue方法獲得值,這就是Enum類型的獨特地方。再看下面:
ls("com.cbf4life.common.command.LSCommand");
是不是很特別?是的,枚舉的基本功能就是定義默認可選值,但是Java中的枚舉功能又增強了很多,可以添加方法和屬性,基本上就是一個特殊的類。若要詳細了解Enum,讀者可以翻閱一下相關語法書。
現在剩下的工作就是寫一個Client類,然后看看運行情況如何,如代碼清單34-13所示。
代碼清單34-13 場景類
public?class?Client?{
?????public?static?void?main(String[]?args)?throws?IOException?{
?????????????Invoker?invoker?=?new?Invoker();
?????????????while(true){
?????????????????????//UNIX下的默認提示符號
?????????????????????System.out.print("#");
?????????????????????//捕獲輸出
?????????????????????String?input?=?(new?BufferedReader(new?InputStreamReader?(System.in))).readLine();
?????????????????????//輸入quit或exit則退出
?????????????????????if(input.equals("quit")?||?input.equals("exit")){
?????????????????????????????return;
?????????????????????}
?????????????????????System.out.println(invoker.exec(input));
?????????????}
?????}
}
Client也很簡單,通過一個while循環允許使用者持續輸入,然后打印出返回值,運行結果如下:
#ls
file1
file2
file3
file4
#ls -l
drw-rw-rw root system 1024 2009-8-20 10:23 file1
drw-rw-rw root system 1024 2009-8-20 10:23 file2
drw-rw-rw root system 1024 2009-8-20 10:23 file3
#ls -a
.
..
file1
file2
file3
#quit
我們已經實現了在Windows下操作UNIX命令的功能,但是僅僅一個ls命令族是不夠的,我們要擴展,把一百多個命令都擴展出來,怎么擴展呢?現在增加一個df命令族,顯示磁盤的大小,只要增加類圖就成,如圖34-5所示。

圖34-5 擴展df命令后的類圖
僅僅增加了粗框的部分,也就是增加DFCommand、AbstractDF以及實現類就可以完成擴展功能。先看AbstractDF代碼,如代碼清單34-14所示。
代碼清單34-14 df命令的抽象類
public?abstract?class?AbstractDF?extends?CommandName?{
?????//默認參數
?????public?final?static?String?DEFAULT_PARAM?=?"";
?????//參數k
?????public?final?static?String?K_PARAM?=?"k";
?????//參數g
?????public?final?static?String?G_PARAM?=?"g";
}
與前面一樣的功能,定義選項名稱。接下來是三個實現類,都非常簡單,如代碼清單34-15所示。
代碼清單34-15 df命令的具體實現類
public?class?DF?extends?AbstractDF{
?????//定義一下自己能處理什么參數
?????protected?String?getOperateParam()?{
?????????????return?super.DEFAULT_PARAM;
?????}
?????//命令處理
?????protected?String?echo(CommandVO?vo)?{
?????????????return?DiskManager.df();
?????}
}
public?class?DF_K?extends?AbstractDF{
?????//定義一下自己能處理什么參數
?????protected?String?getOperateParam()?{
?????????????return?super.K_PARAM;
?????}
?????//命令處理
?????protected?String?echo(CommandVO?vo)?{
?????????????return?DiskManager.df_k();
?????}
}
public?class?DF_G?extends?AbstractDF{
?????//定義一下自己能處理什么參數
?????protected?String?getOperateParam()?{
?????????????return?super.G_PARAM;
?????}
?????//命令處理
?????protected?String?echo(CommandVO?vo)?{
?????????????return?DiskManager.df_g();
?????}
}
每個選項的實現類都定義了自己能解析什么命令,然后通過echo方法返回執行結果。在三個實現類中都與DiskManager類有關聯關系,該類負責與操作系統有關的功能,是必須要實現的,其示例代碼如代碼清單34-16所示。
代碼清單34-16 磁盤管理
public?class?DiskManager?{
?????//默認的計算大小
?????public?static?String?df(){
?????????????return?"/\t10485760\n/usr\t104857600\n/home\t1048576000\n";
?????}
?????//按照kb來計算
?????public?static?String?df_k(){
?????????????return?"/\t10240\n/usr\t102400\n/home\tt10240000\n";
?????}
?????//按照gb計算
?????public?static?String?df_g(){
?????????????return?"/\t10\n/usr\t100\n/home\tt10000\n";
?????}
}
以上為示例代碼,若要實際計算磁盤大小,可以使用JNI的方式或者執行操作系統的命令的方式獲得,特別是JDK 1.6提供了獲得一個root目錄大小的方法。
然后再增加一個DFCommand命令,負責執行命令,如代碼清單34-17所示。
代碼清單34-17 可執行的df命令
public?class?DFCommand?extends?Command?{
?????public?String?execute(CommandVO?vo)?{
?????????????return?super.buildChain(AbstractDF.class).get(0).handleMessage(vo);
?????}
}
最后一步,修改一下CommandEnum配置,增加一個枚舉項,如代碼清單34-18所示。
代碼清單34-18 增加后的枚舉項
public?enum?CommandEnum?{
?????ls("com.cbf4life.common.command.LSCommand"),
?????df("com.cbf4life.common.command.DFCommand");
?????private?String?value?=?"";?????
?????//定義構造函數,目的是Data(value)類型的相匹配
?????private?CommandEnum(String?value){
?????????????this.value?=?value;
?????}
?????public?String?getValue(){
?????????????return?this.value;
?????}
?????//返回所有的enum對象
?????public?static?List<String>?getNames(){
?????????????CommandEnum[]?commandEnum?=?CommandEnum.values();
?????????????List<String>?names?=?new?ArrayList<String>();
?????????????for(CommandEnum?c:commandEnum){
?????????????????????names.add(c.name());
?????????????}
?????????????return?names;
?????}
}
運行結果如下所示:
#ls
file1
file2
file3
file4
#df
/ 10485760
/usr 104857600
/home 1048576000
#df -k
/ 10240
/usr 102400
/home t10240000
#df -g
/ 10
/usr 100
/home t10000
#
僅僅增加類就完成了變更,這才是我們要的結果:對修改關閉,對擴展開放。
- 前言
- 第一部分 大旗不揮,誰敢沖鋒——6大設計原則全新解讀
- 第1章 單一職責原則
- 1.2 絕殺技,打破你的傳統思維
- 1.3 我單純,所以我快樂
- 1.4 最佳實踐
- 第2章 里氏替換原則
- 2.2 糾紛不斷,規則壓制
- 2.3 最佳實踐
- 第3章 依賴倒置原則
- 3.2 言而無信,你太需要契約
- 3.3 依賴的三種寫法
- 3.4 最佳實踐
- 第4章 接口隔離原則
- 4.2 美女何其多,觀點各不同
- 4.3 保證接口的純潔性
- 4.4 最佳實踐
- 第5章 迪米特法則
- 5.2 我的知識你知道得越少越好
- 5.3 最佳實踐
- 第6章 開閉原則
- 6.2 開閉原則的廬山真面目
- 6.3 為什么要采用開閉原則
- 6.4 如何使用開閉原則
- 6.5 最佳實踐
- 第二部分 真刀實槍 ——23種設計模式完美演繹
- 第7章 單例模式
- 7.2 單例模式的定義
- 7.3 單例模式的應用
- 7.4 單例模式的擴展
- 7.5 最佳實踐
- 第8章 工廠方法模式
- 8.2 工廠方法模式的定義
- 8.3 工廠方法模式的應用
- 8.4 工廠方法模式的擴展
- 8.5 最佳實踐
- 第9章 抽象工廠模式
- 9.2 抽象工廠模式的定義
- 9.3 抽象工廠模式的應用
- 9.4 最佳實踐
- 第10章 模板方法模式
- 10.2 模板方法模式的定義
- 10.3 模板方法模式的應用
- 10.4 模板方法模式的擴展
- 10.5 最佳實踐
- 第11章 建造者模式
- 11.2 建造者模式的定義
- 11.3 建造者模式的應用
- 11.4 建造者模式的擴展
- 11.5 最佳實踐
- 第12章 代理模式
- 12.2 代理模式的定義
- 12.3 代理模式的應用
- 12.4 代理模式的擴展
- 12.5 最佳實踐
- 第13章 原型模式
- 13.2 原型模式的定義
- 13.3 原型模式的應用
- 13.4 原型模式的注意事項
- 13.5 最佳實踐
- 第14章 中介者模式
- 14.2 中介者模式的定義
- 14.3 中介者模式的應用
- 14.4 中介者模式的實際應用
- 14.5 最佳實踐
- 第15章 命令模式
- 15.2 命令模式的定義
- 15.3 命令模式的應用
- 15.4 命令模式的擴展
- 15.5 最佳實踐
- 第16章 責任鏈模式
- 16.2 責任鏈模式的定義
- 16.3 責任鏈模式的應用
- 16.4 最佳實踐
- 第17章 裝飾模式
- 17.2 裝飾模式的定義
- 17.3 裝飾模式應用
- 17.4 最佳實踐
- 第18章 策略模式
- 18.2 策略模式的定義
- 18.3 策略模式的應用
- 18.4 策略模式的擴展
- 18.5 最佳實踐
- 第19章 適配器模式
- 19.2 適配器模式的定義
- 19.3 適配器模式的應用
- 19.4 適配器模式的擴展
- 19.5 最佳實踐
- 第20章 迭代器模式
- 20.2 迭代器模式的定義
- 20.3 迭代器模式的應用
- 20.4 最佳實踐
- 第21章 組合模式
- 21.2 組合模式的定義
- 21.3 組合模式的應用
- 21.4 組合模式的擴展
- 21.5 最佳實踐
- 第22章 觀察者模式
- 22.2 觀察者模式的定義
- 22.3 觀察者模式的應用
- 22.4 觀察者模式的擴展
- 22.5 最佳實踐
- 第23章 門面模式
- 23.2 門面模式的定義
- 23.3 門面模式的應用
- 23.4 門面模式的注意事項
- 23.5 最佳實踐
- 第24章 備忘錄模式
- 24.2 備忘錄模式的定義
- 24.3 備忘錄模式的應用
- 24.4 備忘錄模式的擴展
- 24.5 最佳實踐
- 第25章 訪問者模式
- 25.2 訪問者模式的定義
- 25.3 訪問者模式的應用
- 25.4 訪問者模式的擴展
- 25.5 最佳實踐
- 第26章 狀態模式
- 26.2 狀態模式的定義
- 26.3 狀態模式的應用
- 第27章 解釋器模式
- 27.2 解釋器模式的定義
- 27.3 解釋器模式的應用
- 27.4 最佳實踐
- 第28章 享元模式
- 28.2 享元模式的定義
- 28.3 享元模式的應用
- 28.4 享元模式的擴展
- 28.5 最佳實踐
- 第29章 橋梁模式
- 29.2 橋梁模式的定義
- 29.3 橋梁模式的應用
- 29.4 最佳實踐
- 第三部分 誰的地盤誰做主 ——設計模式PK
- 第30章 創建類模式大PK
- 30.1 工廠方法模式VS建造者模式
- 30.2 抽象工廠模式VS建造者模式
- 第31章 結構類模式大PK
- 31.1 代理模式VS裝飾模式
- 31.2 裝飾模式VS適配器模式
- 第32章 行為類模式大PK
- 32.1 命令模式VS策略模式
- 32.2 策略模式VS狀態模式
- 32.3 觀察者模式VS責任鏈模式
- 第33章 跨戰區PK
- 33.1 策略模式VS橋梁模式
- 33.2 門面模式VS中介者模式
- 33.3 包裝模式群PK
- 第四部分 完美世界 ——設計模式混編
- 第34章 命令模式+責任鏈模式
- 34.2 混編小結
- 第35章 工廠方法模式+策略模式
- 35.2 混編小結
- 第36章 觀察者模式+中介者模式
- 36.2 混編小結
- 第五部分 擴展篇
- 第37章 MVC框架
- 37.2 最佳實踐
- 第38章 新模式
- 38.1 規格模式
- 38.2 對象池模式
- 38.3 雇工模式
- 38.4 黑板模式
- 38.5 空對象模式
- 附錄 23種設計模式彩圖