<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                第34章 命令模式+責任鏈模式 34.1 搬移UNIX的命令 在操作系統的世界里,有兩大陣營一直在PK著:*nix(包括UNIX和Linux)和Windows。從目前的統計數據來看,*nix在應用服務器領域占據相對優勢,不過Windows也不甘示弱,國內某些小型銀行已經在使用PC Server(安裝Windows操作系統的服務器)集群來進行銀行業務運算,而且穩定性、性能各方面的效果不錯;而在個人桌面方面,Windows是占絕對優勢的,大家應該基本上都在用這個操作系統,它的諸多優點這里就不多說了,我們今天就來解決一個習慣問題。如果你負責過UNIX系統維護,你自己的筆記本又是Windows操作系統的話,我想你肯定有這樣的經驗,如圖34-1所示。 ![](https://box.kancloud.cn/2016-08-14_57b0036f9c36d.jpg) 圖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所示。 ![](https://box.kancloud.cn/2016-08-14_57b0036fb751c.jpg) 圖34-2 命令族的解析類圖 類圖還是比較清晰的,UNIX的命令有上百個,我們定義一個CommandName抽象類,所有的命令都繼承于該類,它就是責任鏈模式的handler類,負責鏈表控制;每個命令族都有一個獨立的抽象類,因為每個命令族都有其獨特的個性,比如ls命令和df命令,其后可加的參數是不一樣的,這就可以在抽象類AbstractLS中定義,而且它還有標示作用,標示其下的實現類都是實現ls命令的,只是命令的選項不同;Context負責建立一條命令的鏈表,比如ls命令族、df命令族等,它組裝出一個處理一個命令族的責任鏈,并返回首節點供高層模塊調用,這是非常典型的責任鏈模式。 分析完畢一個具體的命令族,已經確定可以采用責任鏈模式,我們繼續往下分析。UNIX命令非常多,敲一個命令返回一個結果,每個具體的命令可以由相關的命令族(也就是責任鏈)來解析,但是如此多的命令還是需要有一個派發的角色,輸入一個命令,不管后臺誰來解析,返回一個結果就成,這就要用到命令模式。命令模式負責協調各個命令正確地傳遞到各個責任鏈的首節點,這就是它的任務,其類圖如圖34-3所示。 ![](https://box.kancloud.cn/2016-08-14_57b0036fccb28.jpg) 圖34-3 命令傳遞類圖 是不是典型的命令模式類圖?其中Chain是一個標示符,表示的就是我們上面分析的責任鏈,每一個具體的命令負責調用責任鏈的首節點,獲得返回值,結束命令的執行。兩個核心模塊都分析完畢了,就可以把類圖融合在一起,完整的類圖如圖34-4所示。 這個類圖還是比較簡單的,我們來看一下各個類的職責。 ● ClassUtils ClassUtils是工具類,其主要職責是根據一個接口、父類查找到所有的子類。在不考慮效率的應用中,使用該類可以帶來非常好的擴展性。 ● CommandVO CommandVO是命令的值對象,它把一個命令解析為命令名、選項、操作數,例如"ls-l/usr"命令分別解析為getCommandName、getParam、getData三個方法的返回值。 ● CommandEnum CommandEnum是枚舉類型,是主要的命令配置文件。為什么需要枚舉類型?這是JDK 1.5提供的一個非常好的功能,我們在程序中再講解如何使用它。 所有的分析都已經完成了,我們來看看程序。程序不復雜,看看類圖,應該先寫命令的解釋,這是項目的核心。我們先來看CommandName抽象類,如代碼清單34-1所示。 ![](https://box.kancloud.cn/2016-08-14_57b00370022dd.jpg) 圖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所示。 ![](https://box.kancloud.cn/2016-08-14_57b003701921f.jpg) 圖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 # 僅僅增加類就完成了變更,這才是我們要的結果:對修改關閉,對擴展開放。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看