Ⅰ [探討]提高代碼質量的方法有哪些
1. 代碼風格和規范:
多看看網上的一些代碼規范,仔細思考一下制定這些規范的出發點是什麼。例如Oracle(前
SUN)公司的代碼規范,Google的代碼規范 。
2. 學習最佳實踐
在編碼中遇到的各種大大小小的問題,首先不是自己去「閉門造車」的冥思苦想,多用
Google,搜搜是否已經有現成的解決方案。
3. 閱讀優秀的開源代碼
網上有很多優秀的開源項目,針對你自己項目中遇到的問題,找找類似的開源項目,學習、
研究,最重要的是變成自己的東西。
4. 學好英語
英語是目前所有編程語言的基礎。你的文件名、類名、方法名、變數名都是需要一個好的英
語基礎才能夠起得合適。任何的業務邏輯,都需要你使用以英語為基礎的計算機語言表達出來。
英語不好,你的代碼永遠看起來不專業。
書籍涵蓋的主題從個人責任、職業發展,知道用於使代碼保持靈活、並且易於改編和復用的
各種架構技術。
總結起來方法有三:平時多練手,多閱讀優秀的源碼,閱讀優秀的編程技巧類型的書。
Ⅱ 軟體工程 程序員保證代碼質量的手段的先後順序是怎樣的
保證代碼質量的順序如下:
1)代碼風格
在項目開發之初,往往會制定一個代碼編寫的規范,實際上,這個代碼規范就包含了整個項目組的代碼風格。由於軟體開發人員的設計習慣不同,如果不統一代碼風格,一個項目中的代碼將五花八門,如變數和常量的命名、介面與實現類的注釋、何時回車、怎樣縮進等等。一個五花八門的設計風格,必將為日後的維護與改進帶來困難。
通過代碼復查,一方面督促開發人員按照規范編寫代碼,另一方面也使開發人員自身形成良好的編程習慣。代碼風格的審查,由於內容比較單一,常常可以通過一些代碼復查的工具來自動完成,提高復查的效率。
2)重大缺陷
在一些關於代碼復查的文章中,列出了一個常常的單子,描述了代碼復查應當著重注意的重大缺陷,它們包括:存在SQL注入、易受跨站點腳本攻擊、緩存區溢出、託管代碼等等。項目組可以不斷積累重大缺陷的審查項目,並在每次審查中逐一檢查。重大缺陷審查是一個繁瑣而細致的工作,如果能編寫或使用一些審查軟體,可以大大提高審查效率。
3)設計邏輯與思路的審查
審查是代碼復查中最核心、最有價值的部分。代碼風格與重大缺陷的審查,雖然重要但簡單而機械,可以通過軟體自動檢查;而設計邏輯與思路的審查,卻是復雜而有深度的審查,需要有一定理論深度和編碼經驗的人才能完成,而且對新手尤其重要。前面提到,新手是任何項目組不可避免的問題。
通過代碼復查,讓老手去指導新手,讓團隊整體素質得到提高。具體辦法就是,在新手完成編碼以後,讓老手去進行代碼復查,指出新手的問題,指導新手設計。這樣的過程最初可能需要重構,甚至重新編碼。但經過這樣的過程,新手將逐漸熟練,迅速成為老手,使整體團隊素質提高。
Ⅲ 如何提高代碼質量6
高質量代碼的三要素 我們評價高質量代碼有三要素:可讀性、可維護性、可變更性。我們的代碼要一個都不能少地達到了這三要素的要求才能算高質量的代碼。1.可讀性強 一提到可讀性似乎有一些老生常談的味道,但令人沮喪的是,雖然大家一而再,再而三地強調可讀性,但我們的代碼在可讀性方面依然做得非常糟糕。由於工作的需要,我常常需要去閱讀他人的代碼,維護他人設計的模塊。每當我看到大段大段、密密麻麻的代碼,而且還沒有任何的注釋時常常感慨不已,深深體會到了這項工作的重要。由於分工的需要,我們寫的代碼難免需要別人去閱讀和維護的。而對於許多程序員來說,他們很少去閱讀和維護別人的代碼。正因為如此,他們很少關注代碼的可讀性,也對如何提高代碼的可讀性缺乏切身體會。有時即使為代碼編寫了注釋,也常常是注釋語言晦澀難懂形同天書,令閱讀者反復斟酌依然不明其意。針對以上問題,我給大家以下建議:1)不要編寫大段的代碼 如果你有閱讀他人代碼的經驗,當你看到別人寫的大段大段的代碼,而且還不怎麼帶注釋,你是怎樣的感覺,是不是「嗡」地一聲頭大。各種各樣的功能糾纏在一個方法中,各種變數來回調用,相信任何人多不會認為它是高質量的代碼,但卻頻繁地出現在我們編寫的程序了。如果現在你再回顧自己寫過的代碼,你會發現,稍微編寫一個復雜的功能,幾百行的代碼就出去了。一些比較好的辦法就是分段。將大段的代碼經過整理,分為功能相對獨立的一段又一段,並且在每段的前端編寫一段注釋。這樣的編寫,比前面那些雜亂無章的大段代碼確實進步了不少,但它們在功能獨立性、可復用性、可維護性方面依然不盡人意。從另一個比較專業的評價標准來說,它沒有實現低耦合、高內聚。我給大家的建議是,將這些相對獨立的段落另外封裝成一個又一個的函數。許多大師在自己的經典書籍中,都鼓勵我們在編寫代碼的過程中應當養成不斷重構的習慣。我們在編寫代碼的過程中常常要編寫一些復雜的功能,起初是寫在一個類的一個函數中。隨著功能的逐漸展開,我們開始對復雜功能進行歸納整理,整理出了一個又一個的獨立功能。這些獨立功能有它與其它功能相互交流的輸入輸出數據。當我們分析到此處時,我們會非常自然地要將這些功能從原函數中分離出來,形成一個又一個獨立的函數,供原函數調用。在編寫這些函數時,我們應當仔細思考一下,為它們取一個釋義名稱,並為它們編寫注釋(後面還將詳細討論這個問題)。另一個需要思考的問題是,這些函數應當放到什麼地方。這些函數可能放在原類中,也可能放到其它相應職責的類中,其遵循的原則應當是「職責驅動設計」(後面也將詳細描述)。下面是我編寫的一個從XML文件中讀取數據,將其生成工廠的一個類。這個類最主要的一段程序就是初始化工廠,該功能歸納起來就是三部分功能:用各種方式嘗試讀取文件、以DOM的方式解析XML數據流、生成工廠。而這些功能被我歸納整理後封裝在一個不同的函數中,並且為其取了釋義名稱和編寫了注釋:Java代碼 /** * 初始化工廠。根據路徑讀取XML文件,將XML文件中的數據裝載到工廠中 * @param path XML的路徑 */ public void initFactory(String path){ if(findOnlyOneFileByClassPath(path)){return;} if(findResourcesByUrl(path)){return;} if(findResourcesByFile(path)){return;} this.paths = new String[]{path}; } /** * 初始化工廠。根據路徑列表依次讀取XML文件,將XML文件中的數據裝載到工廠中 * @param paths 路徑列表 */ public void initFactory(String[] paths){ for(int i=0; i<paths.length; i++){ initFactory(paths[i]); } this.paths = paths; } /** * 重新初始化工廠,初始化所需的參數,為上一次初始化工廠所用的參數。 */ public void reloadFactory(){ initFactory(this.paths); } /** * 採用ClassLoader的方式試圖查找一個文件,並調用<code>readXmlStream()</code>進行解析 * @param path XML文件的路徑 * @return 是否成功 */ protected boolean findOnlyOneFileByClassPath(String path){ boolean success = false; try { Resource resource = new ClassPathResource(path, this.getClass()); resource.setFilter(this.getFilter()); InputStream is = resource.getInputStream(); if(is==null){return false;} readXmlStream(is); success = true; } catch (SAXException e) { log.debug("Error when findOnlyOneFileByClassPath:"+path,e); } catch (IOException e) { log.debug("Error when findOnlyOneFileByClassPath:"+path,e); } catch (ParserConfigurationException e) { log.debug("Error when findOnlyOneFileByClassPath:"+path,e); } return success; } /** * 採用URL的方式試圖查找一個目錄中的所有XML文件,並調用<code>readXmlStream()</code>進行解析 * @param path XML文件的路徑 * @return 是否成功 */ protected boolean findResourcesByUrl(String path){ boolean success = false; try { ResourcePath resourcePath = new PathMatchResource(path, this.getClass()); resourcePath.setFilter(this.getFilter()); Resource[] loaders = resourcePath.getResources(); for(int i=0; i<loaders.length; i++){ InputStream is = loaders[i].getInputStream(); if(is!=null){ readXmlStream(is); success = true; } } } catch (SAXException e) { log.debug("Error when findResourcesByUrl:"+path,e); } catch (IOException e) { log.debug("Error when findResourcesByUrl:"+path,e); } catch (ParserConfigurationException e) { log.debug("Error when findResourcesByUrl:"+path,e); } return success; } /** *用File的方式試圖查找文件,並調用<code>readXmlStream()</code>解析 * @param path XML文件的路徑 * @return 是否成功 */ protected boolean findResourcesByFile(String path){ boolean success = false; FileResource loader = new FileResource(new File(path)); loader.setFilter(this.getFilter()); try { Resource[] loaders = loader.getResources(); if(loaders==null){return false;} for(int i=0; i<loaders.length; i++){ InputStream is = loaders[i].getInputStream(); if(is!=null){ readXmlStream(is); success = true; } } } catch (IOException e) { log.debug("Error when findResourcesByFile:"+path,e); } catch (SAXException e) { log.debug("Error when findResourcesByFile:"+path,e); } catch (ParserConfigurationException e) { log.debug("Error when findResourcesByFile:"+path,e); } return success; } /** * 讀取並解析一個XML的文件輸入流,以Element的形式獲取XML的根, * 然後調用<code>buildFactory(Element)</code>構建工廠 * @param inputStream 文件輸入流 * @throws SAXException * @throws IOException * @throws ParserConfigurationException */ protected void readXmlStream(InputStream inputStream) throws SAXException, IOException, ParserConfigurationException{ if(inputStream==null){ throw new ParserConfigurationException("Cann't parse source because of InputStream is null!"); } DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(this.isValidating()); factory.setNamespaceAware(this.isNamespaceAware()); DocumentBuilder build = factory.newDocumentBuilder(); Document doc = build.parse(new InputSource(inputStream)); Element root = doc.getDocumentElement(); buildFactory(root); } /** * 用從一個XML的文件中讀取的數據構建工廠 * @param root 從一個XML的文件中讀取的數據的根 */ protected abstract void buildFactory(Element root); /** * 初始化工廠。根據路徑讀取XML文件,將XML文件中的數據裝載到工廠中 * @param path XML的路徑 */ public void initFactory(String path){ if(findOnlyOneFileByClassPath(path)){return;} if(findResourcesByUrl(path)){return;} if(findResourcesByFile(path)){return;} this.paths = new String[]{path}; } /** * 初始化工廠。根據路徑列表依次讀取XML文件,將XML文件中的數據裝載到工廠中 * @param paths 路徑列表 */ public void initFactory(String[] paths){ for(int i=0; i<paths.length; i++){ initFactory(paths[i]); } this.paths = paths; } /** * 重新初始化工廠,初始化所需的參數,為上一次初始化工廠所用的參數。 */ public void reloadFactory(){ initFactory(this.paths); } /** * 採用ClassLoader的方式試圖查找一個文件,並調用<code>readXmlStream()</code>進行解析 * @param path XML文件的路徑 * @return 是否成功 */ protected boolean findOnlyOneFileByClassPath(String path){ boolean success = false; try { Resource resource = new ClassPathResource(path, this.getClass()); resource.setFilter(this.getFilter()); InputStream is = resource.getInputStream(); if(is==null){return false;} readXmlStream(is); success = true; } catch (SAXException e) { log.debug("Error when findOnlyOneFileByClassPath:"+path,e); } catch (IOException e) { log.debug("Error when findOnlyOneFileByClassPath:"+path,e); } catch (ParserConfigurationException e) { log.debug("Error when findOnlyOneFileByClassPath:"+path,e); } return success; } /** * 採用URL的方式試圖查找一個目錄中的所有XML文件,並調用<code>readXmlStream()</code>進行解析 * @param path XML文件的路徑 * @return 是否成功 */ protected boolean findResourcesByUrl(String path){ boolean success = false; try { ResourcePath resourcePath = new PathMatchResource(path, this.getClass()); resourcePath.setFilter(this.getFilter()); Resource[] loaders = resourcePath.getResources(); for(int i=0; i<loaders.length; i++){ InputStream is = loaders[i].getInputStream(); if(is!=null){ readXmlStream(is); success = true; } } } catch (SAXException e) { log.debug("Error when findResourcesByUrl:"+path,e); } catch (IOException e) { log.debug("Error when findResourcesByUrl:"+path,e); } catch (ParserConfigurationException e) { log.debug("Error when findResourcesByUrl:"+path,e); } return success; } /** * 用File的方式試圖查找文件,並調用<code>readXmlStream()</code>解析 * @param path XML文件的路徑 * @return 是否成功 */ protected boolean findResourcesByFile(String path){ boolean success = false; FileResource loader = new FileResource(new File(path)); loader.setFilter(this.getFilter()); try { Resource[] loaders = loader.getResources(); if(loaders==null){return false;} for(int i=0; i<loaders.length; i++){ InputStream is = loaders[i].getInputStream(); if(is!=null){ readXmlStream(is); success = true; } } } catch (IOException e) { log.debug("Error when findResourcesByFile:"+path,e); } catch (SAXException e) { log.debug("Error when findResourcesByFile:"+path,e); } catch (ParserConfigurationException e) { log.debug("Error when findResourcesByFile:"+path,e); } return success; } /** * 讀取並解析一個XML的文件輸入流,以Element的形式獲取XML的根, * 然後調用<code>buildFactory(Element)</code>構建工廠 * @param inputStream 文件輸入流 * @throws SAXException * @throws IOException * @throws ParserConfigurationException */ protected void readXmlStream(InputStream inputStream) throws SAXException, IOException, ParserConfigurationException{ if(inputStream==null){ throw new ParserConfigurationException("Cann't parse source because of InputStream is null!"); } DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(this.isValidating()); factory.setNamespaceAware(this.isNamespaceAware()); DocumentBuilder build = factory.newDocumentBuilder(); Document doc = build.parse(new InputSource(inputStream)); Element root = doc.getDocumentElement(); buildFactory(root); } /** * 用從一個XML的文件中讀取的數據構建工廠 * @param root 從一個XML的文件中讀取的數據的根 */ protected abstract void buildFactory(Element root); 完整代碼在附件中。在編寫代碼的過程中,通常有兩種不同的方式。一種是從下往上編寫,也就是按照順序,每分出去一個函數,都要將這個函數編寫完,才回到主程序,繼續往下編寫。而一些更有經驗的程序員會採用另外一種從上往下的編寫方式。當他們在編寫程序的時候,每個被分出去的程序,可以暫時只寫一個空程序而不去具體實現功能。當主程序完成以後,再一個個實現它的所有子程序。採用這樣的編寫方式,可以使復雜程序有更好的規劃,避免只見樹木不見森林的弊病。有多少代碼就算大段代碼,每個人有自己的理解。我編寫代碼,每當達到15~20行的時候,我就開始考慮是否需要重構代碼。同理,一個類也不應當有太多的函數,當函數達到一定程度的時候就應該考慮分為多個類了;一個包也不應當有太多的類。。。。。。2)釋義名稱與注釋 我們在命名變數、函數、屬性、類以及包的時候,應當仔細想想,使名稱更加符合相應的功能。我們常常在說,設計一個系統時應當有一個或多個系統分析師對整個系統的包、類以及相關的函數和屬性進行規劃,但在通常的項目中這都非常難於做到。對它們的命名更多的還是程序員來完成。但是,在一個項目開始的時候,應當對項目的命名出台一個規范。譬如,在我的項目中規定,新增記錄用new或add開頭,更新記錄用edit或mod開頭,刪除用del開頭,查詢用find或query開頭。使用最亂的就是get,因此我規定,get開頭的函數僅僅用於獲取類屬性。注釋是每個項目組都在不斷強調的,可是依然有許多的代碼沒有任何的注釋。為什麼呢?因為每個項目在開發過程中往往時間都是非常緊的。在緊張的代碼開發過程中,注釋往往就漸漸地被忽略了。利用開發工具的代碼編寫模板也許可以解決這個問題。用我們常用的MyEclipse為例,在菜單「window>>Preferences>>Java>>Code Style>>Code Templates>>Comments」中,可以簡單的修改一下。「Files」代表的是我們每新建一個文件(可能是類也可能是介面)時編寫的注釋,我通常設定為:Java代碼 /* * created on ${date} */ /* * created on ${date} */「Types」代表的是我們新建的介面或類前的注釋,我通常設定為:Java代碼 /** * * @author ${user} */ /** * * @author ${user} */第一行為一個空行,是用於你寫該類的注釋。如果你採用「職責驅動設計」,這里首先應當描述的是該類的職責。如果需要,你可以寫該類一些重要的方法及其用法、該類的屬性及其中文含義等。${user}代表的是你在windows中登陸的用戶名。如果這個用戶名不是你的名稱,你可以直接寫死為你自己的名稱。其它我通常都保持為默認值。通過以上設定,你在創建類或介面的時候,系統將自動為你編寫好注釋,然後你可以在這個基礎上進行修改,大大提高注釋編寫的效率。同時,如果你在代碼中新增了一個函數時,通過Alt+Shift+J快捷鍵,可以按照模板快速添加註釋。在編寫代碼時如果你編寫的是一個介面或抽象類,我還建議你在@author後面增加@see注釋,將該介面或抽象類的所有實現類列出來,因為閱讀者在閱讀的時候,尋找介面或抽象類的實現類比較困難。Java代碼 /** * 抽象的單表數組查詢實現類,僅用於單表查詢 * @author 范鋼 * @see com.htxx.support.query.DefaultArrayQuery * @see com.htxx.support.query.DwrQuery */ public abstract class ArrayQuery implements ISingleQuery { ... /** * 抽象的單表數組查詢實現類,僅用於單表查詢 * @author 范鋼 * @see com.htxx.support.query.DefaultArrayQuery * @see com.htxx.support.query.DwrQuery */public abstract class ArrayQuery implements ISingleQuery {...2.可維護性 軟體的可維護性有幾層意思,首先的意思就是能夠適應軟體在部署和使用中的各種情況。從這個角度上來說,它對我們的軟體提出的要求就是不能將代碼寫死。1)代碼不能寫死 我曾經見我的同事將系統要讀取的一個日誌文件指定在C盤的一個固定目錄下,如果系統部署時沒有這個目錄以及這個文件就會出錯。如果他將這個決定路徑下的目錄改為相對路徑,或者通過一個屬性文件可以修改,代碼豈不就寫活了。一般來說,我在設計中需要使用日誌文件、屬性文件、配置文件,通常都是以下幾個方式:將文件放到與類相同的目錄,使用ClassLoader.getResource()來讀取;將文件放到classpath目錄下,用File的相對路徑來讀取;使用web.xml或另一個屬性文件來制定讀取路徑。我也曾見另一家公司的軟體要求,在部署的時候必須在C:/bea目錄下,如果換成其它目錄則不能正常運行。這樣的設定常常為軟體部署時帶來許多的麻煩。如果伺服器在該目錄下已經沒有多餘空間,或者已經有其它軟體,將是很撓頭的事情。2)預測可能發生的變化 除此之外,在設計的時候,如果將一些關鍵參數放到配置文件中,可以為軟體部署和使用帶來更多的靈活性。要做到這一點,要求我們在軟體設計時,應當更多地有更多的意識,考慮到軟體應用中可能發生的變化。比如,有一次我在設計財務軟體的時候,考慮到一些單據在製作時的前置條件,在不同企業使用的時候,可能要求不一樣,有些企業可能要求嚴格些而有些要求鬆散些。考慮到這種可能的變化,我將前置條件設計為可配置的,就可能方便部署人員在實際部署中進行靈活變化。然而這樣的配置,必要的注釋說明是非常必要的。軟體的可維護性的另一層意思就是軟體的設計便於日後的變更。這一層意思與軟體的可變更性是重合的。所有的軟體設計理論的發展,都是從軟體的可變更性這一要求逐漸展開的,它成為了軟體設計理論的核心。3.可變更性 前面我提到了,軟體的變更性是所有軟體理論的核心,那麼什麼是軟體的可變更性呢?按照現在的軟體理論,客戶對軟體的需求時時刻刻在發生著變化。當軟體設計好以後,為應對客戶需求的變更而進行的代碼修改,其所需要付出的代價,就是軟體設計的可變更性。由於軟體合理地設計,修改所付出的代價越小,則軟體的可變更性越好,即代碼設計的質量越高。一種非常理想的狀態是,無論客戶需求怎樣變化,軟體只需進行適當地修改就能夠適應。但這之所以稱之為理想狀態,因為客戶需求變化是有大有小的。如果客戶需求變化非常大,即使再好的設計也無法應付,甚至重新開發。然而,客戶需求的適當變化,一個合理地設計可以使得變更代價最小化,延續我們設計的軟體的生命力。1)通過提高代碼復用提高可維護性 我曾經遇到過這樣一件事,我要維護的一個系統因為應用范圍的擴大,它對機關級次的計算方式需要改變一種策略。如果這個項目統一採用一段公用方法來計算機關級次,這樣一個修改實在太簡單了,就是修改這個公用方法即可。但是,事實卻不一樣,對機關級次計算的代碼遍布整個項目,甚至有些還寫入到了那些復雜的SQL語句中。在這樣一種情況下,這樣一個需求的修改無異於需要遍歷這個項目代碼。這樣一個實例顯示了一個項目代碼復用的重要,然而不幸的是,代碼無法很好復用的情況遍布我們所有的項目。代碼復用的道理十分簡單,但要具體運作起來非常復雜,它除了需要很好的代碼規劃,還需要持續地代碼重構。對整個系統的整體分析與合理規劃可以根本地保證代碼復用。系統分析師通過用例模型、領域模型、分析模型的一步一步分析,最後通過正向工程,生成系統需要設計的各種類及其各自的屬性和方法。採用這種方法,功能被合理地劃分到這個類中,可以很好地保證代碼復用。採用以上方法雖然好,但技術難度較高,需要有高深的系統分析師,並不是所有項目都能普遍採用的,特別是時間比較緊張的項目。通過開發人員在設計過程中的重構,也許更加實用。當某個開發人員在開發一段代碼時,發現該功能與前面已經開發功能相同,或者部分相同。這時,這個開發人員可以對前面已經開發的功能進行重構,將可以通用的代碼提取出來,進行相應地改造,使其具有一定的通用性,便於各個地方可以使用。一些比較成功的項目組會指定一個專門管理通用代碼的人,負責收集和整理項目組中各個成員編寫的,可以通用的代碼。這個負責人同時也應當具有一定的代碼編寫功力,因為將專用代碼提升為通用代碼,或者以前使用該通用代碼的某個功能,由於業務變更,而對這個通用代碼的變更要求,都對這個負責人提出了很高的能力要求。雖然後一種方式非常實用,但是它有些亡羊補牢的味道,不能從整體上對項目代碼進行有效規劃。正因為兩種方法各有利弊,因此在項目中應當配合使用。2)利用設計模式提高可變更性 對於初學者,軟體設計理論常常感覺晦澀難懂。一個快速提高軟體質量的捷徑就是利用設計模式。這里說的設計模式,不僅僅指經典的32個模式,是一切前人總結的,我們可以利用的、更加廣泛的設計模式。a. if...else...這個我也不知道叫什麼名字,最早是哪位大師總結的,它出現在Larman的《UML與模式應用》,也出現在出現在Mardin的《敏捷軟體開發》。它是這樣描述的:當你發現你必須要設計這樣的代碼:「if...elseif...elseif...else...」時,你應當想到你的代碼應當重構一下了。我們先看看這樣的代碼有怎樣的特點。Java代碼 if(var.equals("A")){ doA(); }else if(var.equals("B")){ doB(); }else if(var.equals("C")){ doC(); }else{ doD(); } if(var.equals("A")){ doA(); }else if(var.equals("B")){ doB(); }else if(var.equals("C")){ doC(); }else{ doD(); }這樣的代碼很常見,也非常平常,我們大家都寫過。但正是這樣平常才隱藏著我們永遠沒有注意的問題。問題就在於,如果某一天這個選項不再僅僅是A、B、C,而是增加了新的選項,會怎樣呢?你也許會說,那沒有關系,我把代碼改改就行。然而事實上並非如此,在大型軟體研發與維護中有一個原則,每次的變更盡量不要去修改原有的代碼。如果我們重構一下,能保證不修改原有代碼,僅僅增加新的代碼就能應付選項的增加,這就增加了這段代碼的可維護性和可變更性,提高了代碼質量。那麼,我們應當如何去做呢?經過深入分析你會發現,這里存在一個對應關系,即A對應doA(),B對應doB()...如果將doA()、doB()、doC()...與原有代碼解耦,問題就解決了。如何解耦呢?設計一個介面X以及它的實現A、B、C...每個類都包含一個方法doX(),並且將doA()的代碼放到A.doX()中,將doB()的代碼放到B.doX()中...經過以上的重構,代碼還是這些代碼,效果卻完全不一樣了。我們只需要這樣寫:Java代碼 X x = factory.getBean(var); x.doX(); X x = factory.getBean(var);x.doX();這樣就可以實現以上的功能了。我們看到這里有一個工廠,放著所有的A、B、C...並且與它們的key對應起來,並且寫在配置文件中。如果出現新的選項時,通過修改配置文件就可以無限制的增加下去。這個模式雖然有效提高了代碼質量,但是不能濫用,並非只要出現if...else...就需要使用。由於它使用了工廠,一定程度上增加了代碼復雜度,因此僅僅在選項較多,並且增加選項的可能性很大的情況下才可以使用。另外,要使用這個模式,繼承我在附件中提供的抽象類XmlBuildFactoryFacade就可以快速建立一個工廠。如果你的項目放在spring或其它可配置框架中,也可以快速建立工廠。設計一個Map靜態屬性並使其V為這些A、B、C...這個工廠就建立起來了。b.策略模式 也許你看過策略模式(strategy model)的相關資料但沒有留下太多的印象。一個簡單的例子可以讓你快速理解它。如果一個員工系統中,員工被分為臨時工和正式工並且在不同的地方相應的行為不一樣。在設計它們的時候,你肯定設計一個抽象的員工類,並且設計兩個繼承類:臨時工和正式工。這樣,通過下塑類型,可以在不同的地方表現出臨時工和正式工的各自行為。在另一個系統中,員工被分為了銷售人員、技術人員、管理人員並且也在不同的地方相應的行為不一樣。同樣,我們在設計時也是設計一個抽象的員工類,並且設計數個繼承類:銷售人員、技術人員、管理人員。現在,我們要把這兩個系統合並起來,也就是說,在新的系統中,員工既被分為臨時工和正式工,又被分為了銷售人員、技術人員、管理人員,這時候如何設計。如果我們還是使用以往的設計,我們將不得不設計很多繼承類:銷售臨時工、銷售正式工、技術臨時工、技術正式工...如此的設計,在隨著劃分的類型,以及每種類型的選項的增多,呈笛卡爾增長。通過以上一個系