




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
高質(zhì)量代碼的三要素我們?cè)u(píng)價(jià)高質(zhì)量代碼有三要素:可讀性、可維護(hù)性、可變更性。我們的代碼要一個(gè)都不能少地達(dá)到了這三要素的要求才能算高質(zhì)量的代碼??勺x性強(qiáng)一提到可讀性似乎有一些老生常談的味道,但令人沮喪的是,雖然大家一而再,再而三地強(qiáng)調(diào)可讀性,但我們的代碼在可讀性方面依然做得比較糟糕。每當(dāng)我看到大段大段、密密麻麻的代碼,而且還沒有任何的注釋時(shí)常常感慨不已,深深體會(huì)到了這項(xiàng)工作的重要。由于分工的需要,我們寫的代碼難免需要?jiǎng)e人去閱讀和維護(hù)的。而對(duì)于許多程序員來說,他們很少去閱讀和維護(hù)別人的代碼。正因?yàn)槿绱?,他們很少關(guān)注代碼的可讀性,也對(duì)如何提高代碼的可讀性缺乏切身體會(huì)。有時(shí)即使為代碼編寫了注釋,也常常是注釋語言晦澀難懂形同天書,令閱讀者反復(fù)斟酌依然不明其意。針對(duì)以上問題,我給大家以下建議:1)不要編寫大段的代碼如果你有閱讀他人代碼的經(jīng)驗(yàn),當(dāng)你看到別人寫的大段大段的代碼,而且還不怎么帶注釋,你是怎樣的感覺,是不是“嗡”地一聲頭大。各種各樣的功能糾纏在一個(gè)方法中,各種變量來回調(diào)用,相信任何人都不會(huì)認(rèn)為它是高質(zhì)量的代碼,但卻頻繁地出現(xiàn)在我們編寫的程序了。如果現(xiàn)在你再回顧自己寫過的代碼,你會(huì)發(fā)現(xiàn),稍微編寫一個(gè)復(fù)雜的功能,幾百行的代碼就出去了。一些比較好的辦法就是分段。將大段的代碼經(jīng)過整理,分為功能相對(duì)獨(dú)立的一段又一段,并且在每段的前端編寫一段注釋。這樣的編寫,比前面那些雜亂無章的大段代碼確實(shí)進(jìn)步了不少,但它們?cè)诠δ塥?dú)立性、可復(fù)用性、可維護(hù)性方面依然不盡人意。從另一個(gè)比較專業(yè)的評(píng)價(jià)標(biāo)準(zhǔn)來說,它沒有實(shí)現(xiàn)低耦合、高內(nèi)聚。我給大家的建議是,將這些相對(duì)獨(dú)立的段落另外封裝成一個(gè)又一個(gè)的函數(shù)。許多大師在自己的經(jīng)典書籍中,都鼓勵(lì)我們?cè)诰帉懘a的過程中應(yīng)當(dāng)養(yǎng)成不斷重構(gòu)的習(xí)慣。我們?cè)诰帉懘a的過程中常常要編寫一些復(fù)雜的功能,起初是寫在一個(gè)類的一個(gè)函數(shù)中。隨著功能的逐漸展開,我們開始對(duì)復(fù)雜功能進(jìn)行歸納整理,整理出了一個(gè)又一個(gè)的獨(dú)立功能。這些獨(dú)立功能有它與其它功能相互交流的輸入輸出數(shù)據(jù)。當(dāng)我們分析到此處時(shí),我們會(huì)非常自然地要將這些功能從原函數(shù)中分離出來,形成一個(gè)又一個(gè)獨(dú)立的函數(shù),供原函數(shù)調(diào)用。在編寫這些函數(shù)時(shí),我們應(yīng)當(dāng)仔細(xì)思考一下,為它們?nèi)∫粋€(gè)釋義名稱,并為它們編寫注釋(后面還將詳細(xì)討論這個(gè)問題)。另一個(gè)需要思考的問題是,這些函數(shù)應(yīng)當(dāng)放到什么地方。這些函數(shù)可能放在原類中,也可能放到其它相應(yīng)職責(zé)的類中,其遵循的原則應(yīng)當(dāng)是“職責(zé)驅(qū)動(dòng)設(shè)計(jì)”(后面也將詳細(xì)描述)。在編寫代碼的過程中,通常有兩種不同的方式。一種是從下往上編寫,也就是按照順序,每分出去一個(gè)函數(shù),都要將這個(gè)函數(shù)編寫完,才回到主程序,繼續(xù)往下編寫。而一些更有經(jīng)驗(yàn)的程序員會(huì)采用另外一種從上往下的編寫方式。當(dāng)他們?cè)诰帉懗绦虻臅r(shí)候,每個(gè)被分出去的程序,可以暫時(shí)只寫一個(gè)空程序而不去具體實(shí)現(xiàn)功能。當(dāng)主程序完成以后,再一個(gè)個(gè)實(shí)現(xiàn)它的所有子程序。采用這樣的編寫方式,可以使復(fù)雜程序有更好的規(guī)劃,避免只見樹木不見森林的弊病。有多少代碼就算大段代碼,每個(gè)人有自己的理解。我編寫代碼,每當(dāng)達(dá)到15?20行的時(shí)候,我就開始考慮是否需要重構(gòu)代碼。同理,一個(gè)類也不應(yīng)當(dāng)有太多的函數(shù),當(dāng)函數(shù)達(dá)到一定程度的時(shí)候就應(yīng)該考慮分為多個(gè)類了;一個(gè)包也不應(yīng)當(dāng)有太多的類在aside的源碼中,對(duì)包的組織是比較規(guī)范的,但很多action中存在大量大段代碼的函數(shù)并且缺少注釋,比較典型的有ModuleInfoAction、FileInfoUploadAction,QueryModelAction。里面都有好幾百行的函數(shù),甚至一■個(gè)700行的類一個(gè)函數(shù)就占了一大半。2)釋義名稱與注釋我們?cè)诿兞?、函?shù)、屬性、類以及包的時(shí)候,應(yīng)當(dāng)仔細(xì)想想,使名稱更加符合相應(yīng)的功能。我們常常在說,設(shè)計(jì)一個(gè)系統(tǒng)時(shí)應(yīng)當(dāng)有一個(gè)或多個(gè)系統(tǒng)分析師對(duì)整個(gè)系統(tǒng)的包、類以及相關(guān)的函數(shù)和屬性進(jìn)行規(guī)劃,但在通常的項(xiàng)目中這都非常難于做到。對(duì)它們的命名更多的還是程序員來完成。但是,在一個(gè)項(xiàng)目開始的時(shí)候,應(yīng)當(dāng)對(duì)項(xiàng)目的命名出臺(tái)一個(gè)規(guī)范。譬如,新增記錄用new或add開頭,更新記錄用edit或mod開頭,刪除用del開頭,查詢用find或query開頭。使用最亂的就是get,get開頭的函數(shù)應(yīng)該僅僅用于獲取類屬性。在aside中,ModuleInfoAction這個(gè)類的命名就非常奇怪,這是一個(gè)用來做走查的類,卻起了這樣一個(gè)名字,看的我非常迷惑。MissionAction,MissionManager中的命名也非常古怪,getMissionTargets02,getMissionTargets這樣的函數(shù)命名很難理解注釋是每個(gè)項(xiàng)目組都在不斷強(qiáng)調(diào)的,可是依然有許多的代碼沒有任何的注釋。為什么呢?因?yàn)槊總€(gè)項(xiàng)目在開發(fā)過程中往往時(shí)間都是非常緊的。在緊張的代碼開發(fā)過程中,注釋往往就漸漸地被忽略了。利用開發(fā)工具的代碼編寫模板也許可以解決這個(gè)問題。用我們常用的Eclipse為例,在菜單“window>>Preferences>>Java>>CodeStyle>>CodeTemplates>>Comments”中,可以簡單的修改一下。
*Preferences口回區(qū)typ曰filtertextCodeTemplates(+)■■〔;4直巨l?:il?-Ant*Preferences口回區(qū)typ曰filtertextCodeTemplates(+)■■〔;4直巨l?:il?-Ant({]■■Help(±J-IrLstiill/Update0-Java由Apps=xr:iii':eQ-Eui1-1Path\Q-ColeStyleCle:iTLUpCodeTemplatesFLl2_g!iTLizMImportsCumpilerDebugEditorInst=ills-iJTLEsJadClips^JIJrLitFruper11eeFilesEditc+MjrIclipse+Plug-1hlIlHveLupnierLt?-Kutl,[母獎(jiǎng)?-Tgam匚Qnfi幻jt日FroiHiCtSizwciEic3日tting;..CuntigurugynpratedcodeJindccimrriuTLtc:一CummerLtEFilesh!s!3..3..s.ITypesPieldsConstruetorsMtthiidEOveriidingmethodwHeLegatemethmdi■jettersSetters住ICEt*createdon-(date}C;iTLcel“Files”代表的是我們每新建一個(gè)文件(可能是類也可能是接口)時(shí)編寫的注釋,我通常設(shè)定為:Java代碼TOC\o"1-5"\h\z/**createdon${date}*/“Types”代表的是我們新建的接口或類前的注釋,我通常設(shè)定為:Java代碼/****@author$(user}*/第一行為一個(gè)空行,是用于你寫該類的注釋。如果你采用“職責(zé)驅(qū)動(dòng)設(shè)計(jì)”,這里首先應(yīng)當(dāng)描述的是該類的職責(zé)。如果需要,你可以寫該類一些重要的方法及其用法、該類的屬性及其中文含義等。${user}代表的是你在windows中登陸的用戶名。如果這個(gè)用戶名不是你的名稱,你可以直接寫死為你自己的名稱。其它我通常都保持為默認(rèn)值。通過以上設(shè)定,你在創(chuàng)建類或接口的時(shí)候,系統(tǒng)將自動(dòng)為你編寫好注釋,然后你可以在這個(gè)基礎(chǔ)上進(jìn)行修改,大大提高注釋編寫的效率。同時(shí),如果你在代碼中新增了一個(gè)函數(shù)時(shí),通過Alt+Shift+J快捷鍵,可以按照模板快速添加注釋。可維護(hù)性軟件的可維護(hù)性有幾層意思,首先的意思就是能夠適應(yīng)軟件在部署和使用中的各種情況。從這個(gè)角度上來說,它對(duì)我們的軟件提出的要求就是不能將代碼寫死。1)代碼不能寫死應(yīng)該通過定義一些常量或者通過一個(gè)屬性文件可以修改,也不要把所有變量都定義在一個(gè)地方,對(duì)于一些有限制的屬性,定義成int或string是非常難維護(hù)的并且不容易在編譯期檢查到錯(cuò)誤,java5開始提供了枚舉這種數(shù)據(jù)類型,我們要善于利用。據(jù)一個(gè)例子,baseLine.setStatus("3”);不可讀,一旦狀態(tài)發(fā)生變化,需要各處去改,用IDE無法重構(gòu),維護(hù)性很差,而且容易錯(cuò)。在hibernate中有?Enumerated可以直接用來映射,可讀性,可維護(hù)性會(huì)好很多,而且可以避免用戶輸入了亂七八糟的字符。同樣的例子還有phase這個(gè)屬性。后面在Constants中定義了一些變量提高了可讀性,但是同樣要求調(diào)用者對(duì)用哪個(gè)Constants的變量很清楚,并且不能約束調(diào)用者必須要用,不能提供約束。2)預(yù)測可能發(fā)生的變化除此之外,在設(shè)計(jì)的時(shí)候,如果將一些關(guān)鍵參數(shù)放到配置文件中,可以為軟件部署和使用帶來更多的靈活性。要做到這一點(diǎn),要求我們?cè)谲浖O(shè)計(jì)時(shí),應(yīng)當(dāng)更多地有更多的意識(shí),考慮到軟件應(yīng)用中可能發(fā)生的變化。軟件的可維護(hù)性的另一層意思就是軟件的設(shè)計(jì)便于日后的變更。這一層意思與軟件的可變更性是重合的。所有的軟件設(shè)計(jì)理論的發(fā)展,都是從軟件的可變更性這一要求逐漸展開的,它成為了軟件設(shè)計(jì)理論的核心。但是在這里還要提醒的一點(diǎn)就是過度設(shè)計(jì)帶來的問題,如果我們?cè)谠O(shè)計(jì)任何功能時(shí)都考慮以后會(huì)不會(huì)有變化,然后去設(shè)計(jì)一個(gè)框架去適應(yīng)這些變化,那么可能把一些簡單的問題給復(fù)雜化了,因?yàn)閷?shí)現(xiàn)一個(gè)功能和實(shí)現(xiàn)一個(gè)框架,然后基于這個(gè)框架實(shí)現(xiàn)這個(gè)功能在工作量上是有天壤之別的。這個(gè)就需要對(duì)需求要有充分的了解,并且根據(jù)經(jīng)驗(yàn)取得平衡。大多數(shù)情況下直接實(shí)現(xiàn)功能,在必要的時(shí)候進(jìn)行重構(gòu)是比較好的方法。但如果沒有持續(xù)重構(gòu)的保證,走一步看一步會(huì)把代碼寫得亂七八糟??勺兏郧懊嫖姨岬搅耍浖淖兏允撬熊浖碚摰暮诵?,那么什么是軟件的可變更性呢?按照現(xiàn)在的軟件理論,客戶對(duì)軟件的需求時(shí)時(shí)刻刻在發(fā)生著變化。當(dāng)軟件設(shè)計(jì)好以后,為應(yīng)對(duì)客戶需求的變更而進(jìn)行的代碼修改,其所需要付出的代價(jià),就是軟件設(shè)計(jì)的可變更性。由于軟件合理地設(shè)計(jì),修改所付出的代價(jià)越小,則軟件的可變更性越好,即代碼設(shè)計(jì)的質(zhì)量越高。一種非常理想的狀態(tài)是,無論客戶需求怎樣變化,軟件只需進(jìn)行適當(dāng)?shù)匦薷木湍軌蜻m應(yīng)。但這之所以稱之為理想狀態(tài),因?yàn)榭蛻粜枨笞兓怯写笥行〉?。如果客戶需求變化非常大,即使再好的設(shè)計(jì)也無法應(yīng)付,甚至重新開發(fā)。然而,客戶需求的適當(dāng)變化,一個(gè)合理地設(shè)計(jì)可以使得變更代價(jià)最小化,延續(xù)我們?cè)O(shè)計(jì)的軟件的生命力。1)通過提高代碼復(fù)用提高可變更性我們對(duì)于各數(shù)據(jù)項(xiàng)的操作在各個(gè)模塊的代碼中都可以看到,甚至有些還寫入到了那些復(fù)雜的SQL語句中。在這樣一種情況下,如果對(duì)數(shù)據(jù)項(xiàng)的命名、分類等邏輯進(jìn)行修改無異于需要遍歷這個(gè)項(xiàng)目代碼。代碼復(fù)用的道理十分簡單,但要具體運(yùn)作起來非常復(fù)雜,它除了需要很好的代碼規(guī)劃,還需要持續(xù)地代碼重構(gòu)。對(duì)整個(gè)系統(tǒng)的整體分析與合理規(guī)劃可以根本地保證代碼復(fù)用。系統(tǒng)分析師通過用例模型、領(lǐng)域模型、分析模型的一步一步分析,最后通過正向工程,生成系統(tǒng)需要設(shè)計(jì)的各種類及其各自的屬性和方法。采用這種方法,功能被合理地劃分到這個(gè)類中,可以很好地保證代碼復(fù)用。采用以上方法雖然好,但技術(shù)難度較高,需要有高深的系統(tǒng)分析師,并不是所有項(xiàng)目都能普遍采用的,特別是時(shí)間比較緊張的項(xiàng)目。通過開發(fā)人員在設(shè)計(jì)過程中的重構(gòu),也許更加實(shí)用。當(dāng)某個(gè)開發(fā)人員在開發(fā)一段代碼時(shí),發(fā)現(xiàn)該功能與前面已經(jīng)開發(fā)功能相同,或者部分相同。這時(shí),這個(gè)開發(fā)人員可以對(duì)前面已經(jīng)開發(fā)的功能進(jìn)行重構(gòu),將可以通用的代碼提取出來,進(jìn)行相應(yīng)地改造,使其具有一定的通用性,便于各個(gè)地方可以使用。一些比較成功的項(xiàng)目組會(huì)指定一個(gè)專門管理通用代碼的人,負(fù)責(zé)收集和整理項(xiàng)目組中各個(gè)成員編寫的,可以通用的代碼。這個(gè)負(fù)責(zé)人同時(shí)也應(yīng)當(dāng)具有一定的代碼編寫功力,因?yàn)閷S么a提升為通用代碼,或者以前使用該通用代碼的某個(gè)功能,由于業(yè)務(wù)變更,而對(duì)這個(gè)通用代碼的變更要求,都對(duì)這個(gè)負(fù)責(zé)人提出了很高的能力要求。雖然后一種方式非常實(shí)用,但是它有些亡羊補(bǔ)牢的味道,不能從整體上對(duì)項(xiàng)目代碼進(jìn)行有效規(guī)劃。正因?yàn)閮煞N方法各有利弊,因此在項(xiàng)目中應(yīng)當(dāng)配合使用。2)職責(zé)驅(qū)動(dòng)設(shè)計(jì)前面我提到,當(dāng)我們嘗試寫一些復(fù)雜功能的時(shí)候,我們把功能分解成一個(gè)個(gè)相對(duì)獨(dú)立的函數(shù)。但是,應(yīng)當(dāng)將這些函數(shù)分配到哪個(gè)類中呢?也就是系統(tǒng)中的所有類都應(yīng)當(dāng)擁有哪些函數(shù)呢?或者說應(yīng)當(dāng)表現(xiàn)出哪些行為呢?答案就在這里:以職責(zé)為中心,根據(jù)職責(zé)分配行為。我們?cè)诜治鱿到y(tǒng)時(shí),首先是根據(jù)客戶需求進(jìn)行用例分析,然后根據(jù)用例繪制領(lǐng)域模式和分析模型,整個(gè)系統(tǒng)最主要的類就形成了。通過以上分析形成的類,往往和現(xiàn)實(shí)世界的對(duì)象是對(duì)應(yīng)的。正因?yàn)槿绱?,軟件世界的這些類也具有了與現(xiàn)實(shí)世界的對(duì)象相對(duì)應(yīng)的職責(zé),以及在這些職責(zé)范圍內(nèi)的行為。職責(zé)驅(qū)動(dòng)設(shè)計(jì)的核心思想,就是我們?cè)趯?duì)一個(gè)系統(tǒng)進(jìn)行分析設(shè)計(jì)的時(shí)候,應(yīng)當(dāng)以職責(zé)為中心,根據(jù)職責(zé)分配行為。這種思想首先要求我們?cè)O(shè)計(jì)的所有軟件世界的對(duì)象,應(yīng)當(dāng)與現(xiàn)實(shí)世界盡量保持一致,稱為“低表示差異”。有了低表示差異,一方面提高了代碼的可讀性,另一方面,當(dāng)業(yè)務(wù)發(fā)生變更的時(shí)候,也可以根據(jù)實(shí)際情況快速應(yīng)對(duì)變更。代碼設(shè)計(jì)的目標(biāo):低耦合,高內(nèi)聚耦合就是對(duì)某元素與其它元素之間的連接、感知和依賴的量度。耦合包括:元素B是元素A的屬性,或者元素A引用了元素B的實(shí)例(這包括元素A調(diào)用的某個(gè)方法,其參數(shù)中包含元素B)。元素A調(diào)用了元素B的方法。元素A直接或間接成為元素B的子類。元素A是接口B的實(shí)現(xiàn)。如果一個(gè)元素過于依賴其它元素,一旦它所依賴的元素不存在,或者發(fā)生變更,則該元素將不能再正常運(yùn)行,或者不得不相應(yīng)地進(jìn)行變更。因此,耦合將大大影響代碼的通用性和可變更性。內(nèi)聚,更為專業(yè)的說法叫功能內(nèi)聚,是對(duì)軟件系統(tǒng)中元素職責(zé)相關(guān)性和集中度的度量。如果元素具有高度相關(guān)的職責(zé),除了這些職責(zé)內(nèi)的任務(wù),沒有其它過多的工作,那么該元素就具有高內(nèi)聚性,反之則為低內(nèi)聚性。內(nèi)聚就像一個(gè)
管理者,它只做自己職責(zé)范圍內(nèi)的事,而將其它與它相關(guān)的事情,分配給別人去做。高質(zhì)量的代碼要求我們的代碼保持低耦合、高內(nèi)聚。但是,這個(gè)要求是如此的抽象與模糊,如何才能做到這些呢?軟件大師們告訴我們了許多方法,其中之一就是職責(zé)驅(qū)動(dòng)設(shè)計(jì)。要理解職責(zé)驅(qū)動(dòng)設(shè)計(jì),我們首先要理解“低表示差異”。低表示差異軟件世界我們開發(fā)的應(yīng)用軟件實(shí)際上是對(duì)現(xiàn)實(shí)世界的模擬,因此,軟件世界與現(xiàn)實(shí)世界存在著必然的聯(lián)系。如果我們?cè)谲浖治龊驮O(shè)計(jì)的過程中,將軟件世界與現(xiàn)實(shí)世界緊密地聯(lián)系到一起,我們的軟件將更加本色地還原事物最本質(zhì)的規(guī)律。這樣的設(shè)計(jì),就稱之為“低表示差異”。軟件世界現(xiàn)實(shí)世界采用“低表示差異”進(jìn)行軟件設(shè)計(jì),現(xiàn)實(shí)世界有什么事物,就映射為軟件世界的各種對(duì)象(類);現(xiàn)實(shí)世界的事物擁有什么樣的職責(zé),在軟件世界里的對(duì)象就擁有什么樣的職責(zé);在現(xiàn)實(shí)世界中的事物,因?yàn)樗穆氊?zé)而產(chǎn)生的行為,在軟件世界中就反映為對(duì)象所擁有的函數(shù)。低表示差異,使分析設(shè)計(jì)者對(duì)軟件的分析和設(shè)計(jì)更加簡單,思路更加清晰;使代碼更加可讀,閱讀者更加易于理解;更重要的是,當(dāng)需求發(fā)生變更,或者業(yè)務(wù)產(chǎn)生擴(kuò)展時(shí),設(shè)計(jì)者只需要遵循事物本來的面貌去思考和修改軟件,使軟件更加易于變更和擴(kuò)展。角色、職責(zé)、協(xié)作理解了“低表示差異”,現(xiàn)在我們來看看我們應(yīng)當(dāng)如何運(yùn)用職責(zé)驅(qū)動(dòng)設(shè)計(jì)進(jìn)行分析和設(shè)計(jì)。首先,我們通過與客戶的溝通和對(duì)業(yè)務(wù)需求的了解,從中提取出現(xiàn)實(shí)世界中的關(guān)鍵事物以及相互之間的關(guān)系。這個(gè)過程我們通常通過建立領(lǐng)域模型來完成。領(lǐng)域模型建立起來以后,通過諸如RationalRose這樣的設(shè)計(jì)軟件的正向工程,生成了我們?cè)谲浖到y(tǒng)中最初始的軟件類。這些軟件類,由于每個(gè)都扮演著現(xiàn)實(shí)世界中的一個(gè)具體的角色,因而賦予了各自的職責(zé)。前面我已經(jīng)提到,如果你的系統(tǒng)采用職責(zé)驅(qū)動(dòng)設(shè)計(jì)的思想進(jìn)行設(shè)計(jì)開發(fā),作為一個(gè)好的習(xí)慣,你應(yīng)當(dāng)在每一個(gè)軟件類的注釋首行,清楚地描述該軟件類的職責(zé)。當(dāng)我們完成了系統(tǒng)中軟件類的制訂,分配好了各自的職責(zé),我們就應(yīng)該開始根據(jù)軟件需求,編寫各個(gè)軟件類的功能。在前面我給大家提出了一個(gè)建議,就是不要在一個(gè)函數(shù)中編寫大段的代碼。編寫大段的代碼,通常會(huì)降低代碼的內(nèi)聚度,因?yàn)檫@些代碼中將包含不是該軟件類應(yīng)當(dāng)完成的工作。作為一個(gè)有經(jīng)驗(yàn)的開發(fā)人員,在編寫一個(gè)功能時(shí),首先應(yīng)當(dāng)對(duì)功能進(jìn)行分解。一段稍微復(fù)雜的功能,通常都可以被分解成一個(gè)個(gè)相對(duì)獨(dú)立的步驟。步驟與步驟之間存在著交互,那就是數(shù)據(jù)的輸入輸出。通過以上的分解,每一個(gè)步驟將形成一個(gè)獨(dú)立的函數(shù),并且使用一個(gè)可以表明這個(gè)步驟意圖的釋義函數(shù)名。接下來,我們應(yīng)當(dāng)考慮的,就是應(yīng)當(dāng)將這些函數(shù)交給誰。它們有可能交給原軟件類,也有可能交給其它軟件類,其分配的原則是什么呢?答案是否清楚,那就是職責(zé)。每個(gè)軟件類代表現(xiàn)實(shí)世界的一個(gè)事物,或者說一個(gè)角色。在現(xiàn)實(shí)世界中這個(gè)任務(wù)應(yīng)當(dāng)由誰來完成,那么在軟件世界中,這個(gè)函數(shù)就應(yīng)當(dāng)分配給相應(yīng)的那個(gè)軟件類。通過以上步驟的分解,一個(gè)功能就分配給了多個(gè)軟件類,相互協(xié)作地完成這個(gè)功能。這樣的分析和設(shè)計(jì),其代碼一定是高內(nèi)聚的和高可讀性的。同時(shí),當(dāng)需求發(fā)生變更的時(shí)候,設(shè)計(jì)者通過對(duì)現(xiàn)實(shí)世界的理解,可以非常輕松地找到那個(gè)需要修改的軟件類,而不會(huì)影響其它類,因而也就變得易維護(hù)、易變更和低耦合了。舉一個(gè)實(shí)例也許更能幫助理解。拿一個(gè)員工工資系統(tǒng)來說吧。當(dāng)人力資源發(fā)放一個(gè)月工資的時(shí)候,以及離職的員工肯定不能再發(fā)放工資了。在系統(tǒng)設(shè)計(jì)的期初,開發(fā)人員商量好,在員工信息中設(shè)定一個(gè)“離職標(biāo)志”字段。編寫工資發(fā)放的開發(fā)人員通過查詢,將“離職標(biāo)志”為false的員工查詢出來,并為他們計(jì)算和發(fā)放工資。但是,隨著這個(gè)系統(tǒng)的不斷使用,編寫員工管理的開發(fā)人員發(fā)現(xiàn),“離職標(biāo)志”字段已經(jīng)不能滿足客戶的需求,因而將“離職標(biāo)志”字段廢棄,并增加了一個(gè)“離職時(shí)間”字段來管理離職的員工。然而,編寫工資發(fā)放的開發(fā)人員并不知道這樣的變更,依然使用著“離職標(biāo)志”字段。顯然,這樣的結(jié)果就是,軟件系統(tǒng)開始對(duì)離職員工發(fā)放工資了。仔細(xì)分析這個(gè)問題的原因,我們不難發(fā)現(xiàn),確認(rèn)員工是否離職,并不是“發(fā)放工資”軟件類應(yīng)當(dāng)完成的工作,而應(yīng)當(dāng)是“員工管理”軟件類應(yīng)當(dāng)完成的。如果將“獲取非離職員工”的任務(wù)交給“員工管理”類,而“發(fā)放工資”軟件類僅僅只是去調(diào)用,那么離職功能由“離職標(biāo)志”字段改為了“離職時(shí)間”字段,其實(shí)就與“發(fā)放工資”軟件類毫無關(guān)系。而作為“員工管理”的開發(fā)人員,一旦發(fā)生這樣的變更,他當(dāng)然知道去修改自己相應(yīng)的“獲取非離職員工”函數(shù),這樣就不會(huì)發(fā)生以上問題。在aside中,同樣存在很多類似的問題,我們每個(gè)模塊對(duì)外暴露的接口基本上就是對(duì)數(shù)據(jù)庫的操作接口,而沒有按照業(yè)務(wù)邏輯進(jìn)行封裝隱藏,一個(gè)模塊的數(shù)據(jù)模型的變化往往導(dǎo)致很多模塊的變更。比如任務(wù)模塊、基線模塊、走查模塊這些通用模塊,應(yīng)該從較高層次上總結(jié)出對(duì)其他模塊的服務(wù)接口,其他模塊要避免直接操作這些模塊的數(shù)據(jù)。我們系統(tǒng)中更多的情況是大家害怕這種變更,不改變以前的邏輯,通過一些奇怪的方法加上新的邏輯,導(dǎo)致代碼的可讀性和可維護(hù)性越來越差。職責(zé)分配與信息專家通過以上對(duì)職責(zé)驅(qū)動(dòng)設(shè)計(jì)的講述,我們不難發(fā)現(xiàn),職責(zé)驅(qū)動(dòng)設(shè)計(jì)的精要就是職責(zé)分配。但是,在紛繁復(fù)雜的軟件設(shè)計(jì)中,如何進(jìn)行職責(zé)分配常常令我們迷惑。信息專家模式(又稱為專家模式)告訴我們,在分析設(shè)計(jì)中,應(yīng)當(dāng)將職責(zé)分配給軟件系統(tǒng)中的這樣一個(gè)軟件類,它擁有實(shí)現(xiàn)這個(gè)職責(zé)所必須的信息。我們稱這個(gè)軟件類,叫“信息專家”。用更加簡短的話說,就是將職責(zé)分配給信息專家。為什么我們要將職責(zé)分配給信息專家呢?我們用上面的例子來說明吧。當(dāng)“發(fā)放工資”軟件類需要獲取非離職員工時(shí),“員工管理”軟件類就是“獲取非離職員工”任務(wù)的信息專家,因?yàn)樗莆罩袉T工的信息。假設(shè)我們不將“獲取非離職員工”的任務(wù)交給“員工管理”軟件類,而是另一個(gè)軟件類X,那么,為了獲取員工信息,軟件類X不得不訪問“員工管理”軟件類,從而使“發(fā)放工資”與X耦合,X又與“員工管理”耦合。這樣的設(shè)計(jì),不如直接將“獲取非離職員工”的任務(wù)交給“員工管理”軟件類,使得“發(fā)放工資”僅僅與“員工管理”耦合,從而有效地降低了系統(tǒng)的整體耦合度。不應(yīng)該把大量的邏輯充斥在Action中,應(yīng)該把邏輯集中在service層,action只應(yīng)該充當(dāng)接收頁面請(qǐng)求,請(qǐng)求服務(wù),準(zhǔn)備下個(gè)頁面的顯示數(shù)據(jù),然后返回結(jié)果。比如SystemRequirementAction.baseline()函數(shù)完全應(yīng)該是baseline模塊提供的服務(wù),提供接口給各模塊使用,而不應(yīng)該放在SystemRequirementAction中做,在baseline模塊,應(yīng)該提供一個(gè)統(tǒng)一的manager,而不是三個(gè)分開的其實(shí)只是封裝了數(shù)據(jù)操作的 manager,BaseLineManager、BaseLineSourceManager、BaseLineTargetManager,這三個(gè)manager本質(zhì)上不能稱之為service??傊?,采用“職責(zé)驅(qū)動(dòng)設(shè)計(jì)”的思路,為我們提高軟件開發(fā)質(zhì)量、可讀性、可維護(hù)性,以及保持軟件的持續(xù)發(fā)展,提供了一個(gè)廣闊的空間。利用設(shè)計(jì)模式提高可變更性一個(gè)快速提高軟件質(zhì)量的捷徑就是利用設(shè)計(jì)模式。這里說的設(shè)計(jì)模式,不僅僅指經(jīng)典的模式,是一切前人總結(jié)的,我們可以利用的、更加廣泛的設(shè)計(jì)模式。a.警惕if...else...我忘了是從哪里學(xué)到的這個(gè)原則,但這個(gè)簡單的原則一直非常有用地指導(dǎo)我的編碼,它是這樣描述的:當(dāng)你發(fā)現(xiàn)你必須要設(shè)計(jì)這樣的代碼:“if...elseif...elseif...else...”時(shí),你應(yīng)當(dāng)想到你的代碼應(yīng)當(dāng)重構(gòu)一下了。我們先看看這樣的代碼有怎樣的特點(diǎn)。Java代碼if(var.equals(〃A〃)){doA();}elseif(var.equals("B"))(doB();}elseif(var.equals("C"))(doC();}else(doD();}這樣的代碼很常見,也非常平常,我們大家都寫過。但正是這樣平常才隱藏著我們永遠(yuǎn)沒有注意的問題。問題就在于,如果某一天這個(gè)選項(xiàng)不再僅僅是A、B、C,而是增加了新的選項(xiàng),會(huì)怎樣呢?你也許會(huì)說,那沒有關(guān)系,我把代碼改改就行。然而事實(shí)上并非如此,在大型軟件研發(fā)與維護(hù)中有一個(gè)原則,每次的變更盡量不要去修改原有的代碼。如果我們重構(gòu)一下,能保證不修改原有代碼,僅僅增加新的代碼就能應(yīng)付選項(xiàng)的增加,這就增加了這段代碼的可維護(hù)性和可變更性,提高了代碼質(zhì)量。那么,我們應(yīng)當(dāng)如何去做呢?經(jīng)過深入分析你會(huì)發(fā)現(xiàn),這里存在一個(gè)對(duì)應(yīng)關(guān)系,即A對(duì)應(yīng)doA(),B對(duì)應(yīng)doB()...如果將doA()、doB()、doC()...與原有代碼解耦,問題就解決了。如何解耦呢?設(shè)計(jì)一個(gè)接口X以及它的實(shí)現(xiàn)A、B、C...每個(gè)類都包含一個(gè)方法doX(),并且將doA()的代碼放到A.doX()中,將doB()的代碼放到B.doX()中...經(jīng)過以上的重構(gòu),代碼還是這些代碼,效果卻完全不一樣了。我們只需要這樣寫:Java代碼Xx=factory.getBean(var);x.doX();這樣就可以實(shí)現(xiàn)以上的功能了。我們看到這里有一個(gè)工廠,放著所有的A、B、C...并且與它們的key對(duì)應(yīng)起來,并且寫在配置文件中。如果出現(xiàn)新的選項(xiàng)時(shí),通過修改配置文件就可以無限制的增加下去。這個(gè)模式雖然有效提高了代碼質(zhì)量,但是不能濫用,并非只要出現(xiàn)if...else...就需要使用。由于它使用了工廠,一定程度上增加了代碼復(fù)雜度,因此僅僅在選項(xiàng)較多,并且增加選項(xiàng)的可能性比較大的情況下才需要使用。一般來說,原則是第一次就這樣寫,當(dāng)發(fā)生了兩次需要增加選項(xiàng)的時(shí)候再去重構(gòu)它。在aside中,有很多這種ifelse的情況,前面我舉的幾個(gè)大函數(shù)的例子就是因?yàn)槔锩嬗写罅康膇felse,可以用這種方法重構(gòu)一下b?策略模式也許你看過策略模式的相關(guān)資料但沒有留下太多的印象。一個(gè)簡單的例子可以讓你快速理解它。如果一個(gè)員工系統(tǒng)中,員工被分為臨時(shí)工和正式工并且在不同的地方相應(yīng)的行為不一樣。在設(shè)計(jì)它們的時(shí)候,你肯定設(shè)計(jì)一個(gè)抽象的員工類,并且設(shè)計(jì)兩個(gè)繼承類:臨時(shí)工和正式工。這樣,通過下塑類型,可以在不同的地方表現(xiàn)出臨時(shí)工和正式工的各自行為。在另一個(gè)系統(tǒng)中,員工被分為了銷售人員、技術(shù)人員、管理人員并且也在不同的地方相應(yīng)的行為不一樣。同樣,我們?cè)谠O(shè)計(jì)時(shí)也是設(shè)計(jì)一個(gè)抽象的員工類,并且設(shè)計(jì)數(shù)個(gè)繼承類:銷售人員、技術(shù)人員、管理人員?,F(xiàn)在,我們要把這兩個(gè)系統(tǒng)合并起來,也就是說,在新的系統(tǒng)中,員工既被分為臨時(shí)工和正式工,又被分為了銷售人員、技術(shù)人員、管理人員,這時(shí)候如何設(shè)計(jì)。如果我們還是使用以往的設(shè)計(jì),我們將不得不設(shè)計(jì)很多繼承類:銷售臨時(shí)工、銷售正式工、技術(shù)臨時(shí)工、技術(shù)正式工...如此的設(shè)計(jì),在隨著劃分的類型,以及每種類型的選項(xiàng)的增多。通過以上一個(gè)系統(tǒng)的設(shè)計(jì),我們不得不發(fā)現(xiàn),我們以往學(xué)習(xí)的關(guān)于繼承的設(shè)計(jì)遇到了挑戰(zhàn)。解決繼承出現(xiàn)的問題,有一個(gè)最好的辦法,就是采用策略模式。在這個(gè)應(yīng)用中,員工之所以要分為臨時(shí)工和正式工,無非是因?yàn)樗鼈兊囊恍┬袨椴灰粯?,比如,發(fā)工資時(shí)的計(jì)算方式不同。如果我們?cè)谠O(shè)計(jì)時(shí)不將員工類分為臨時(shí)工類和正式工類,而僅僅只有員工類,只是在類中增加“工資發(fā)放策略”。當(dāng)我們創(chuàng)建員工對(duì)象時(shí),根據(jù)員工的類型,將“工資發(fā)放策略”設(shè)定為“臨時(shí)工策略”或“正式工策略”,在計(jì)算工資時(shí),只需要調(diào)用策略類中的“計(jì)算工資”方法,其行為的表現(xiàn),也設(shè)計(jì)臨時(shí)工類和正式工類是一樣的。同樣的設(shè)計(jì)可以放到銷售人員策略、技術(shù)人員策略、管理人員策略中。一個(gè)通常的設(shè)計(jì)是,我們將某一個(gè)影響更大的、或者選項(xiàng)更少的屬性設(shè)計(jì)成繼承類,而將其它屬性設(shè)計(jì)成策略類,就可以很好的解決以上問題。使用策略模式,你同樣把代碼寫活了,因?yàn)槟憧梢詿o限制地增加策略。但是,使用策略模式你同樣需要設(shè)計(jì)一個(gè)工廠——策略工廠。以上實(shí)例中,你需要設(shè)計(jì)一個(gè)發(fā)放工資策略工廠,并且在工廠中將“臨時(shí)工”與“臨時(shí)工策略”對(duì)應(yīng)起來,將“正式工”與“正式工策略”對(duì)應(yīng)起來。c.適配器模式水貨手機(jī)充電器的插頭與我們常用的插座不一樣,必須帶一個(gè)適配器,才能使用不同地方的插座。這是一個(gè)對(duì)適配器模式最經(jīng)典的描述。當(dāng)我們?cè)O(shè)計(jì)的系統(tǒng)要與其它系統(tǒng)交互,或者我們?cè)O(shè)計(jì)的模塊要與其它模塊交互時(shí),這種交互可能是調(diào)用一個(gè)接口,或者交換一段數(shù)據(jù),接受方常常因發(fā)送方對(duì)協(xié)議的變更而頻繁變更。這種變更,可能是接受方來源的變更,比如原來是A系統(tǒng),現(xiàn)
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 論文課題申報(bào)書
- 音樂陶笛課題立項(xiàng)申報(bào)書
- 建黨精神課題申報(bào)書
- 古琴課題申報(bào)書
- 快樂讀書吧課題申報(bào)書
- 賣房貸款合同范本
- 咨詢費(fèi)購銷合同范本
- 共享充電寶股合同范本
- 70歲以上用工合同范例
- 品牌童裝贈(zèng)與合同范本
- 美食街道策劃方案
- 河北醫(yī)科大學(xué)第二醫(yī)院招聘工作人員真題
- 三級(jí)醫(yī)院人力資源配置【醫(yī)院人力資源配置方案】
- 《寧夏閩寧鎮(zhèn):昔日干沙灘今日金沙灘》教案- 2023-2024學(xué)年高教版(2023)中職語文職業(yè)模塊
- 數(shù)學(xué)家華羅庚課件
- 彩票風(fēng)險(xiǎn)評(píng)估與控制
- 《中國肌內(nèi)效貼技術(shù)臨床應(yīng)用專家共識(shí)》學(xué)習(xí)分享
- 片上互連優(yōu)化與總線接口設(shè)計(jì)
- 高鈣血癥護(hù)理查房課件
- 圍填海項(xiàng)目生態(tài)保護(hù)修復(fù)方案編制技術(shù)指南(試行)
- 2024年中國包子行業(yè)發(fā)展前景及投資前景預(yù)測報(bào)告(智研咨詢)
評(píng)論
0/150
提交評(píng)論