面向?qū)ο罂▽毜?mdash;—設(shè)計(jì).docx_第1頁(yè)
面向?qū)ο罂▽毜?mdash;—設(shè)計(jì).docx_第2頁(yè)
面向?qū)ο罂▽毜?mdash;—設(shè)計(jì).docx_第3頁(yè)
面向?qū)ο罂▽毜?mdash;—設(shè)計(jì).docx_第4頁(yè)
面向?qū)ο罂▽毜?mdash;—設(shè)計(jì).docx_第5頁(yè)
已閱讀5頁(yè),還剩54頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1. 設(shè)計(jì)原則內(nèi)聚&耦合 前面通過(guò)實(shí)例講解了一個(gè)一環(huán)扣一環(huán)的面向?qū)ο蟮拈_(kāi)發(fā)流程:用例模型 - 領(lǐng)域模型 - 設(shè)計(jì)模型(類(lèi)模型 + 動(dòng)態(tài)模型),解答了面向?qū)ο笕绾巫龅膯?wèn)題。接下來(lái)我們就要講“如何做好面向?qū)ο笤O(shè)計(jì)”的技巧了【內(nèi)聚】參考維基百科的解釋?zhuān)瑑?nèi)聚的含義如下:cohesionreferstothedegreetowhichtheelementsofamodulebelongtogether.翻譯一下即:內(nèi)聚指一個(gè)模塊內(nèi)部元素彼此結(jié)合的緊密程度??雌饋?lái)很好理解,但深入思考一下,其實(shí)沒(méi)有那么簡(jiǎn)單。首先:“模塊”如何理解?你一定會(huì)說(shuō),“模塊”當(dāng)然就是我們所說(shuō)的系統(tǒng)里的“XX模塊”了,例如一個(gè)ERP系統(tǒng)的“權(quán)限”模塊,一個(gè)電子商務(wù)的“支付”模塊,一個(gè)論壇網(wǎng)站的“用戶(hù)管理”模塊。等等你說(shuō)的沒(méi)錯(cuò),但在面向?qū)ο箢I(lǐng)域,談到“內(nèi)聚”的時(shí)候,模塊的概念遠(yuǎn)遠(yuǎn)不止我們通常所理解的“系統(tǒng)內(nèi)的某個(gè)模塊”這個(gè)范圍,而是可大可小,大到一個(gè)子系統(tǒng),小到一個(gè)函數(shù),你都可以理解為內(nèi)聚里所說(shuō)的“模塊”。所以,你可以用“內(nèi)聚”來(lái)判斷一個(gè)函數(shù)設(shè)計(jì)是否合理,一個(gè)類(lèi)設(shè)計(jì)是否合理,一個(gè)接口設(shè)計(jì)是否合理,一個(gè)包的設(shè)計(jì)是否合理,一個(gè)模塊/子系統(tǒng)設(shè)計(jì)是否合理。其次:“元素”究竟是什么?有了前面對(duì)“模塊”的深入研究后,元素的含義就比較容易明確了(不同語(yǔ)言稍有不同)。函數(shù):函數(shù)的元素就是“代碼”類(lèi)/接口:類(lèi)的元素是“函數(shù)、屬性”包:包的元素是“類(lèi)、接口、全局?jǐn)?shù)據(jù)”等模塊:模塊的元素是“包、命名空間”等再次:“結(jié)合”是什么?英文的原文是“belong”,有“屬于”的意思,翻譯成中文“結(jié)合”,更加貼近中文的理解。但“結(jié)合”本身這個(gè)詞容易引起誤解。絕大部分人看到“結(jié)合”這個(gè)單詞,想到的肯定是“你中有我、我中有你”這樣的含義,甚至可能會(huì)聯(lián)想到“美女和帥哥”的結(jié)合,抑或“青蛙王子和公主”的結(jié)合這種情況。這樣的理解本身也并沒(méi)有錯(cuò),但比較狹隘。我們以類(lèi)的設(shè)計(jì)為例:假如一個(gè)類(lèi)里面的函數(shù)都是只依賴(lài)本類(lèi)其它函數(shù)(當(dāng)然不能循環(huán)調(diào)用啦),那內(nèi)聚性肯定是最好的,因?yàn)椤敖Y(jié)合”得很緊密。但如果這個(gè)類(lèi)的函數(shù)并不依賴(lài)本類(lèi)的函數(shù)呢?我們就一定能說(shuō)這個(gè)類(lèi)的內(nèi)聚性不好么?其實(shí)也不盡然,最常見(jiàn)的就是CRUD操作類(lèi),這幾個(gè)函數(shù)相互之間沒(méi)有任何結(jié)合關(guān)系(某些設(shè)計(jì)可能會(huì)先查詢(xún)?cè)傩薷模@樣的設(shè)計(jì)不是事務(wù)安全的),但其實(shí)這幾個(gè)函數(shù)的內(nèi)聚性非常高。所以,關(guān)于內(nèi)聚的結(jié)合概念,我認(rèn)為不是非常恰當(dāng)?shù)拿枋?。那么,就究竟什么才是真正的“?nèi)聚”呢?答案就藏在顯而易見(jiàn)的地方,翻開(kāi)你的詞典,仔細(xì)看看cohesion的含義,你會(huì)看到另外一個(gè)解釋?zhuān)耗哿?!“凝聚力”就是“?nèi)聚”的核心思想,拋開(kāi)面向?qū)ο蟛徽?,我們?nèi)粘9ぷ髦袔缀蹼S處可見(jiàn)“凝聚力”:你可能會(huì)說(shuō),你的團(tuán)隊(duì)很有凝聚力。領(lǐng)導(dǎo)可能會(huì)說(shuō):我們要增強(qiáng)團(tuán)隊(duì)的凝聚力。成功學(xué)大師會(huì)說(shuō):凝聚力是一個(gè)團(tuán)隊(duì)成功的基石。面向?qū)ο箢I(lǐng)域的“凝聚力”,和團(tuán)隊(duì)的“凝聚力”是一樣的概念。判斷團(tuán)隊(duì)凝聚力時(shí),我們關(guān)注團(tuán)隊(duì)成員是否都專(zhuān)注于團(tuán)隊(duì)的目標(biāo);判斷面向?qū)ο竽K的凝聚力時(shí),我們同樣關(guān)注元素是否專(zhuān)注于模塊的目標(biāo),即:模塊本身的職責(zé)!判斷團(tuán)隊(duì)凝聚力時(shí),我們還會(huì)關(guān)注團(tuán)隊(duì)成員之間是否互相吸引和幫助;判斷面向?qū)ο竽K凝聚力時(shí),我們同樣關(guān)注元素間的結(jié)合關(guān)系;雖然判斷內(nèi)聚性的時(shí)候我們會(huì)考慮元素的結(jié)合情況,但其實(shí)是否專(zhuān)注模塊的職責(zé),才是內(nèi)聚性的充要條件。當(dāng)模塊的元素全部都專(zhuān)注于模塊的職責(zé)的時(shí)候,即使元素間的結(jié)合不是很緊密,也是符合內(nèi)聚性的要求的,這也是CRUD設(shè)計(jì)符合內(nèi)聚性的原因。所以,判斷一個(gè)模塊(函數(shù)、類(lèi)、包、子系統(tǒng))“內(nèi)聚性”的高低,最重要的是關(guān)注模塊的元素是否都忠于模塊的職責(zé),簡(jiǎn)單來(lái)說(shuō)就是“不要掛羊頭賣(mài)狗肉”。【耦合】參考維基百科,耦合的定義如下:couplingordependencyisthedegreetowhicheachprogrammodulereliesoneachoneoftheothermodules。簡(jiǎn)單翻譯一下:耦合(或者稱(chēng)依賴(lài))是程序模塊相互之間的依賴(lài)程度。從定義來(lái)看,耦合和內(nèi)聚是相反的:內(nèi)聚關(guān)注模塊內(nèi)部的元素結(jié)合程度,耦合關(guān)注模塊之間的依賴(lài)程度。理解耦合的關(guān)鍵有兩點(diǎn):什么是模塊,什么是依賴(lài)。什么是模塊?模塊和內(nèi)聚里面提到的模塊一樣,耦合中的模塊其實(shí)也是可大可小。常見(jiàn)的模塊有:函數(shù)、類(lèi)、包、子模塊、子系統(tǒng)等什么是依賴(lài)?依賴(lài)這個(gè)詞很好理解,通俗的講就是某個(gè)模塊用到了另外一個(gè)模塊的一些元素。例如:A類(lèi)使用了B類(lèi)作為參數(shù),A類(lèi)的函數(shù)中使用了B類(lèi)來(lái)完成某些功能。等等。2. 高內(nèi)聚低耦合 高內(nèi)聚低耦合,可以說(shuō)是每個(gè)程序猿,甚至是編過(guò)程序,或者僅僅只是在大學(xué)里面學(xué)過(guò)計(jì)算機(jī),都知道的一個(gè)簡(jiǎn)單的設(shè)計(jì)原則。雖然如此流行和人所眾知,但其實(shí)真正理解的人并不多,很多時(shí)候都是人云亦云。要想真正理解“高內(nèi)聚低耦合”,需要回答兩個(gè)問(wèn)題:1)為什么要高內(nèi)聚低耦合?2)高內(nèi)聚低耦合是否意味內(nèi)聚越高越好,耦合越低越好?第一個(gè)問(wèn)題:為什么要高內(nèi)聚低耦合?經(jīng)典的回答是:降低復(fù)雜性。確實(shí)很經(jīng)典,當(dāng)然,其實(shí)也是廢話(huà)!我相信大部分人看了后還是不懂,什么叫復(fù)雜性呢?要回答這個(gè)問(wèn)題,其實(shí)可以采用逆向思維,即:如果我們不做到這點(diǎn),將會(huì)怎樣?首先來(lái)看內(nèi)聚,試想一下,假如我們是低內(nèi)聚,情況將會(huì)如何?前面我們?cè)陉U述內(nèi)聚的時(shí)候提到內(nèi)聚的關(guān)鍵在于“元素的凝聚力”,如果內(nèi)聚性低,則說(shuō)明凝聚力低;對(duì)于一個(gè)團(tuán)隊(duì)來(lái)說(shuō),如果凝聚力低,則一個(gè)明顯的問(wèn)題是“不穩(wěn) 定”;對(duì)于一個(gè)模塊來(lái)說(shuō),內(nèi)聚性低的問(wèn)題也是一樣的“不穩(wěn)定”。具體來(lái)說(shuō)就是如果一個(gè)模塊內(nèi)聚性較低,則這個(gè)模塊很容易變化。一旦變化,設(shè)計(jì)、編碼、測(cè) 試、編譯、部署的工作量就上來(lái)了,而一旦一個(gè)模塊變化,與之相關(guān)的模塊都需要跟著改變。舉一個(gè)簡(jiǎn)單的例子,假設(shè)有這樣一個(gè)設(shè)計(jì)不好的類(lèi):Person,其同時(shí)具有“學(xué)生”、“運(yùn)動(dòng)員”、“演員”3個(gè)職責(zé),有另外3個(gè)類(lèi)“老師”、“教練”、“導(dǎo)演”依賴(lài)這個(gè)類(lèi)。Person.java1. packagecom.oo.cohesion.low;2. 3. /*4. *“人”的類(lèi)設(shè)計(jì)5. *6. */7. publicclassPerson8. 9. /*10. *學(xué)生的職責(zé):學(xué)習(xí)11. */12. publicvoidstudy()13. /TODO:studentsresponsibility14. 15. 16. /*17. *運(yùn)動(dòng)員的職責(zé):運(yùn)動(dòng)18. */19. publicvoidplay()20. /TODO:sportsmansresponsibility21. 22. 23. /*24. *演員的職責(zé):扮演25. */26. publicvoidact()27. /TODO:actorsresponsibity28. 29. Teacher.java1. packagecom.oo.cohesion.low;2. 3. /*4. *“老師”的類(lèi)設(shè)計(jì)5. *6. */7. publicclassTeacher8. 9. publicvoidteach(Personstudent)10. student.study();/依賴(lài)Person類(lèi)的“學(xué)生”相關(guān)的職責(zé)11. 12. Coach.java1. packagecom.oo.cohesion.low;2. 3. /*4. *“教練”的類(lèi)設(shè)計(jì)5. *6. */7. publicclassCoach8. 9. publicvoidtrain(Persontrainee)10. trainee.play();/依賴(lài)Person類(lèi)的“運(yùn)動(dòng)員”職責(zé)11. 12. Director.java1. packagecom.oo.cohesion.low;2. 3. /*4. *“導(dǎo)演”的類(lèi)設(shè)計(jì)5. *6. */7. 8. publicclassDirector9. 10. publicvoiddirect(Personactor)11. actor.act();/依賴(lài)Person類(lèi)“演員”的相關(guān)職責(zé)12. 13. 在上面的樣例中,Person類(lèi)就是一個(gè)典型的“低內(nèi)聚”的類(lèi),很容易發(fā)生改變。比如說(shuō),現(xiàn)在老師要求學(xué)生也要考試,則Person類(lèi)需要新增一個(gè)方法:test,如下:1. packagecom.oo.cohesion.low;2. 3. /*4. *“人”的類(lèi)設(shè)計(jì)5. *6. */7. publicclassPerson8. 9. /*10. *學(xué)生的職責(zé):學(xué)習(xí)11. */12. publicvoidstudy()13. /TODO:studentsresponsibility14. 15. 16. /*17. *學(xué)生的職責(zé):考試18. */19. publicvoidtest()20. /TODO:studentsresponsibility21. 22. 23. /*24. *運(yùn)動(dòng)員的職責(zé):運(yùn)動(dòng)25. */26. publicvoidplay()27. /TODO:sportsmansresponsibility28. 29. 30. /*31. *演員的職責(zé):扮演32. */33. publicvoidact()34. /TODO:actorsresponsibity35. 36. 由于Coach和Director類(lèi)都依賴(lài)于Person類(lèi),Person類(lèi)改變后,雖然這個(gè)改動(dòng)和Coach、Director都沒(méi)有關(guān)系,但Coach和Director類(lèi)都需要重新編譯測(cè)試部署(即使是PHP這樣的腳本語(yǔ)言,至少也要測(cè)試)。同樣,Coach和Director也都可能增加其它對(duì)Person的要求,這樣Person類(lèi)就需要同時(shí)兼顧3個(gè)類(lèi)的業(yè)務(wù)要求,且任何一個(gè)變化,Teacher、Coach、Director都需要重新編譯測(cè)試部署。對(duì)于耦合,我們采用同樣的方式進(jìn)行分析,即:如果高耦合,將會(huì)怎樣?高耦合的情況下,模塊依賴(lài)了大量的其它模塊,這樣任何一個(gè)其它依賴(lài)的模塊變化,模塊本身都需要受到影響。所以,高耦合的問(wèn)題其實(shí)也是“不穩(wěn)定”,當(dāng)然,這個(gè) 不穩(wěn)定和低內(nèi)聚不完全一樣。對(duì)于高耦合的模塊,可能本身并不需要修改,但每次其它模塊修改,當(dāng)前模塊都要編譯、測(cè)試、部署,工作量同樣不小。我們同樣以一個(gè)樣例來(lái)說(shuō)明。假設(shè)我們要設(shè)計(jì)一個(gè)Boss類(lèi),Boss是要管整個(gè)公司的,那么我們假設(shè)這是一家“麻雀雖小五臟俱全”的公司,同時(shí)有“研發(fā)、測(cè)試、技術(shù)支持、銷(xiāo)售、會(huì)計(jì)、行政”等部門(mén)。Boss.java1. packagecom.oo.coupling.high;2. 3. /*4. *“老板”類(lèi)5. *6. */7. publicclassBoss8. 9. privateTestertester;10. privateDeveloperdeveloper;11. privateSupportersupporter;12. privateAdministrationadmin;13. privateAccountantaccountant;14. 15. /*16. *Boss每天檢查工作,看到下面的代碼,是否覺(jué)得Boss很忙?17. */18. publicvoidcheck()19. 20. /檢查測(cè)試工作21. tester.report();22. 23. /檢查研發(fā)工作24. developer.report();25. 26. /檢查技術(shù)支持工作27. supporter.report();28. 29. /檢查行政工作30. admin.report();31. 32. /檢查財(cái)務(wù)工作33. accountant.report();34. 35. Accountant.java1. packagecom.oo.coupling.high;2. 3. /*4. *“財(cái)務(wù)”類(lèi)5. *6. */7. publicclassAccountant8. 9. publicvoidreport()10. System.out.print(Accountantreport);11. 12. Administration.java1. packagecom.oo.coupling.high;2. 3. /*4. *“行政”類(lèi)5. *6. */7. publicclassAdministration8. 9. publicvoidreport()10. System.out.print(Administrationreport);11. 12. Developer.java1. packagecom.oo.coupling.high;2. 3. /*4. *“開(kāi)發(fā)”類(lèi)5. *authorAdministrator6. *7. */8. publicclassDeveloper9. 10. publicvoidreport()11. System.out.print(Developerreport);12. 13. Supporter.java1. packagecom.oo.coupling.high;2. 3. /*4. *“技術(shù)支持”類(lèi)5. *6. */7. publicclassSupporter8. 9. publicvoidreport()10. System.out.print(Supporterreport);11. 12. Tester.java1. packagecom.oo.coupling.high;2. 3. /*4. *“測(cè)試”類(lèi)5. *6. */7. publicclassTester8. 9. publicvoidreport()10. System.out.print(Testerreport);11. 12. 好吧,Boss很忙,我們也很欽佩,但是有一天,研發(fā)的同學(xué)覺(jué)得他們應(yīng)該做更多的事情,于是他們?cè)黾恿艘粋€(gè)“研究”的工作,且這個(gè)工作也不需要報(bào)告給Boss,例如:java view plaincopy1. packagecom.oo.coupling.high;2. 3. /*4. *“開(kāi)發(fā)”類(lèi)5. *authorAdministrator6. *7. */8. publicclassDeveloper9. 10. publicvoidreport()11. System.out.print(Developerreport);12. 13. 14. /*15. *研發(fā)新增加一個(gè)研究的任務(wù),這個(gè)任務(wù)也不需要向Boss匯報(bào)16. */17. publicvoidresearch()18. System.out.print(Developerisresearchingbigdata,hadoop:);19. 20. 雖然這個(gè)工作不需要報(bào)告給Boss,但由于Developer類(lèi)修改了,那么Boss類(lèi)就需要重新編譯測(cè)試部署。其它幾個(gè)Tester、Supporter 類(lèi)等也是類(lèi)似,一旦改變,即使這些類(lèi)和Boss類(lèi)半毛錢(qián)關(guān)系都沒(méi)有,Boss類(lèi)還是需要重新編譯測(cè)試部署,所以Boss類(lèi)是很不穩(wěn)定的。所以,無(wú)論是“低內(nèi)聚”,還是“高耦合”,其本質(zhì)都是“不穩(wěn)定”,不穩(wěn)定就會(huì)帶來(lái)工作量,帶來(lái)風(fēng)險(xiǎn),這當(dāng)然不是我們希望看到的,所以我們應(yīng)該做到“高內(nèi)聚低耦合”。回答完第一個(gè)問(wèn)題后,我們來(lái)看第二個(gè)問(wèn)題:高內(nèi)聚低耦合是否意味著內(nèi)聚越高越好,耦合越低越好?按照我們前面的解釋?zhuān)瑑?nèi)聚越高,一個(gè)類(lèi)越穩(wěn)定;耦合越低,一個(gè)類(lèi)也很穩(wěn)定,所以當(dāng)然是內(nèi)聚越高越好,耦合越低越好了。但其實(shí)稍有經(jīng)驗(yàn)的同學(xué)都會(huì)知道這個(gè)結(jié)論是錯(cuò)誤的,并不是內(nèi)聚越高越好,耦合越低越好,真正好的設(shè)計(jì)是在高內(nèi)聚和低耦合間進(jìn)行平衡,也就是說(shuō)高內(nèi)聚和低耦合是沖突的。我們?cè)敿?xì)來(lái)分析一下為什么高內(nèi)聚和低耦合是沖突的。對(duì)于內(nèi)聚來(lái)說(shuō),最強(qiáng)的內(nèi)聚莫過(guò)于一個(gè)類(lèi)只寫(xiě)一個(gè)函數(shù),這樣內(nèi)聚性絕對(duì)是最高的。但這會(huì)帶來(lái)一個(gè)明顯的問(wèn)題:類(lèi)的數(shù)量急劇增多,這樣就導(dǎo)致了其它類(lèi)的耦合特別多,于是整個(gè)設(shè)計(jì)就變成了“高內(nèi)聚高耦合”了。由于高耦合,整個(gè)系統(tǒng)變動(dòng)同樣非常頻繁。同理,對(duì)于耦合來(lái)說(shuō),最弱的耦合是一個(gè)類(lèi)將所有的函數(shù)都包含了,這樣類(lèi)完全不依賴(lài)其它類(lèi),耦合性是最低的。但這樣會(huì)帶來(lái)一個(gè)明顯的問(wèn)題:內(nèi)聚性很低,于是整個(gè)設(shè)計(jì)就變成了“低耦合低內(nèi)聚”了。由于低內(nèi)聚,整個(gè)類(lèi)的變動(dòng)同樣非常頻繁。對(duì)于“低耦合低內(nèi)聚”來(lái)說(shuō),還有另外一個(gè)明顯的問(wèn)題:幾乎無(wú)法被其它類(lèi)重用。原因很簡(jiǎn)單,類(lèi)本身太龐大了,要么實(shí)現(xiàn)很復(fù)雜,要么數(shù)據(jù)很大,其它類(lèi)無(wú)法明確該如何重用這個(gè)類(lèi)。所以,內(nèi)聚和耦合的兩個(gè)屬性,排列組合一下,只有“高內(nèi)聚低耦合”才是最優(yōu)的設(shè)計(jì)。因此,在實(shí)踐中我們需要牢牢記住需要在高內(nèi)聚和低耦合間進(jìn)行平衡,而不能走極端。 具體如何平衡,且聽(tīng)下回分解。3. SRP原則 前面詳細(xì)闡述了“高內(nèi)聚低耦合”的總體設(shè)計(jì)原則,但如何讓設(shè)計(jì)滿(mǎn)足這個(gè)原則,并不是一件簡(jiǎn)單的事情,幸好各位前輩和大牛已經(jīng)幫我們歸納總結(jié)出來(lái)了,這就是“設(shè)計(jì)原則”和“設(shè)計(jì)模式”。毫不夸張的說(shuō),只要你吃透這些原則和模式并熟練應(yīng)用,就能夠做出很好的設(shè)計(jì)。3.1. 【SRP原則詳解】SRP,singleresponsibilityprinciple,中文翻譯為“單一職責(zé)原則”!這是面向?qū)ο箢?lèi)設(shè)計(jì)的第一個(gè)原則,也是看起來(lái)最簡(jiǎn)單的一個(gè)原則,但這實(shí)際上遠(yuǎn)遠(yuǎn)沒(méi)有那么簡(jiǎn)單,很多人都不一定真正理解了!我們隨便找?guī)讉€(gè)網(wǎng)上的解釋?zhuān)纯锤魑淮髱熁蛘呓?jīng)典網(wǎng)站是如何解釋的:百度百科(/view/1545205.htm):一個(gè)類(lèi)應(yīng)該有且僅有一個(gè)職責(zé)。關(guān)于職責(zé)的含意,面向?qū)ο蟠髱烺obert.C.Martin有一個(gè)著名的定義:所謂一個(gè)類(lèi)的職責(zé)是指引起該類(lèi)變化的原因,如果一個(gè)類(lèi)具有一個(gè)以上的職責(zé),那么就會(huì)有多個(gè)不同的原因引起該類(lèi)變化,其實(shí)就是耦合了多個(gè)互不相關(guān)的職責(zé),就會(huì)降低這個(gè)類(lèi)的內(nèi)聚性。說(shuō)句實(shí)話(huà),雖然是面向?qū)ο蟠髱烳artin的解釋?zhuān)疫€是看得不甚明白:引起類(lèi)變化的原因太多了,例如:給類(lèi)加一個(gè)方法是變化吧?給類(lèi)加一個(gè)屬性是變化吧?類(lèi)的函數(shù)增加一個(gè)參數(shù)是變化吧?。引起這些變化的原因太多了,如果每個(gè)原因都是一個(gè)職責(zé),那SRP原則簡(jiǎn)直就沒(méi)法判斷了!Wiki百科(/wiki/Single_responsibility_principle)內(nèi)容和百度百科基本一致,看起來(lái)百度百科像wiki百科的翻譯:)Martindefinesaresponsibilityasareasontochange,andconcludesthataclassormoduleshouldhaveone,andonlyone,reasontochange.除了這些標(biāo)準(zhǔn)的解釋外,還有一種說(shuō)法:SRP就是指每個(gè)類(lèi)只做一件事!這個(gè)解釋更通俗易懂,也更加適合中國(guó)人理解。雖然比Martin大師的解釋更清楚一些,但仔細(xì)想想,還是有個(gè)地方比較難以理解:什么叫做“一件事”?比如說(shuō)一個(gè)學(xué)生信息管理類(lèi),這個(gè)類(lèi)有“添加學(xué)生信息”、“查詢(xún)學(xué)生信息”、“修改學(xué)生信息”、“刪除學(xué)生信息”,那么這是4件事情,還是一件事情呢?看起來(lái)好像是4個(gè)事情,但稍有經(jīng)驗(yàn)的朋友應(yīng)該都知道,這4個(gè)事情絕大部分情況下都是一個(gè)類(lèi)來(lái)實(shí)現(xiàn)的,而不是分成4個(gè)類(lèi)!所以關(guān)鍵的問(wèn)題在于:什么是“一件事”?是每個(gè)功能一件事情么?其實(shí)答案就在我們自己身上,因?yàn)橹灰覀児ぷ?,無(wú)時(shí)不刻的承擔(dān)著一定的“職責(zé)”!現(xiàn)在讓我們拋開(kāi)面向?qū)ο?,拋開(kāi)軟件,拋開(kāi)計(jì)算機(jī),來(lái)看看我們自己的“職責(zé)”。比如說(shuō),我是一個(gè)程序猿,我的職責(zé)應(yīng)該是“寫(xiě)程序”,但寫(xiě)程序有很多事情,例如:編碼,單元測(cè)試、系統(tǒng)測(cè)試,bug修復(fù),開(kāi)會(huì),寫(xiě)文檔。再比如說(shuō),我的BOSS是一個(gè)管理者,他的職責(zé)是“管理程序猿”,他也有很多工作,例如:制定計(jì)劃,團(tuán)隊(duì)建設(shè)、開(kāi)會(huì)、協(xié)調(diào)資源、寫(xiě)文檔。又比如說(shuō),我是一個(gè)快遞員,也有很多工作:分包、快遞、收款、開(kāi)會(huì)。這些職責(zé)其實(shí)都不是我們自己定義的,而是公司或者部門(mén)或者組織給我們安排工作的時(shí)候定義的,也就是說(shuō):“職責(zé)”是站在他人的角度來(lái)定義的,而不是自己定義的,也許你想把自己定義成CEO,但你的老板會(huì)真的讓你當(dāng)CEO么?經(jīng)過(guò)對(duì)我們自己的職責(zé)的分析,我們可以得出兩個(gè)關(guān)于職責(zé)的重要結(jié)論:1)職責(zé)是站在他人的角度來(lái)定義的2)職責(zé)不是一件事,而是很多事情,但這些事情都是和職責(zé)緊密相關(guān)的對(duì)應(yīng)到面向?qū)ο笤O(shè)計(jì)領(lǐng)域,我們可以說(shuō)一個(gè)類(lèi)的職責(zé)應(yīng)該如下定義:1)類(lèi)的職責(zé)是站在其它類(lèi)的角度來(lái)定義的;2)類(lèi)的職責(zé)包含多個(gè)相關(guān)功能;因此,SRP可以翻譯成“一個(gè)類(lèi)只負(fù)責(zé)一組相關(guān)的事情”,對(duì)應(yīng)到代碼中就是:一個(gè)類(lèi)有多個(gè)方法,這些方法是相關(guān)的。當(dāng)然,如果你再讓我解釋什么是“相關(guān)”,那我只能吐血了:)有了這個(gè)定義,我們?cè)賮?lái)看“學(xué)生信息管理類(lèi)”,很明顯,學(xué)生管理類(lèi)具有的4個(gè)功能都是和“管理”相關(guān)的,按照SRP原則,應(yīng)該只設(shè)計(jì)一個(gè)“學(xué)生信息管理類(lèi)”就可以了。3.2. 【SRP原則范圍】但現(xiàn)實(shí)世界往往比理想要復(fù)雜,一個(gè)最典型的例子就是“辦公一體機(jī)”。根據(jù)SRP原則,打印機(jī)可以設(shè)計(jì)成一個(gè)類(lèi),復(fù)印機(jī)也可以設(shè)計(jì)成一個(gè)類(lèi),掃描儀也可以設(shè)計(jì)成一個(gè)類(lèi),傳真機(jī)還是可以設(shè)計(jì)成一個(gè)類(lèi),但偏偏就是出了個(gè)“辦公一體機(jī)”,這個(gè)機(jī)器集成了“打印、復(fù)印、掃描、傳真”4個(gè)職責(zé)!如果我們要設(shè)計(jì)一個(gè)“辦公一體機(jī)”的類(lèi),怎么也不可能設(shè)計(jì)出一個(gè)符合SRP原則的“辦公一體機(jī)”的類(lèi)來(lái)!怎么辦?是SRP不正確么,還是我們永遠(yuǎn)都不要設(shè)計(jì)“辦公一體機(jī)”這樣的類(lèi)?其實(shí)SRP也沒(méi)有錯(cuò),“辦公一體機(jī)”也應(yīng)該設(shè)計(jì),但:不要用SRP來(lái)約束“辦公一體機(jī)”這樣的類(lèi)!也就是說(shuō),SRP其實(shí)是有適應(yīng)范圍的,SRP只適合那些基礎(chǔ)類(lèi),而不適合基于基礎(chǔ)類(lèi)構(gòu)建復(fù)雜的聚合類(lèi)。在“辦公一體機(jī)“的樣例中,“打印機(jī)”、“復(fù)印機(jī)”、“掃描儀”、“傳真機(jī)”都是基礎(chǔ)類(lèi),每個(gè)類(lèi)都承擔(dān)一個(gè)職責(zé),而辦公一體機(jī)是“聚合類(lèi)”,同時(shí)集成了4種功能!細(xì)心的朋友可能會(huì)繼續(xù)問(wèn)道:SRP不能應(yīng)用于聚合類(lèi),那么如何保證聚合類(lèi)的設(shè)計(jì)質(zhì)量呢?這個(gè)問(wèn)題的答案在GoF的設(shè)計(jì)模式一書(shū)中有詳細(xì)的答案,即:優(yōu)先使用對(duì)象組合,而不是類(lèi)繼承。詳細(xì)內(nèi)容請(qǐng)參考后續(xù)“設(shè)計(jì)模式”部分的內(nèi)容4. OCP原則 開(kāi)閉原則是一個(gè)大部分人都知道,但大部分人都不懂的設(shè)計(jì)原則!OCP,Open-ClosedPrinciple,中文翻譯為“開(kāi)閉原則”。當(dāng)我第一次看到OCP原則時(shí),我的感覺(jué)就是這原則也太抽象了吧,什么開(kāi),什么閉呢?然后我去尋找更加詳細(xì)的答案,最經(jīng)典也是最常見(jiàn)的解釋就是維基百科了:softwareentities(classes,modules,functions,etc.)shouldbeopenforextension,butclosedformodification;翻譯一下就是:對(duì)擴(kuò)展開(kāi)放,對(duì)修改封閉!雖然這句解釋更詳細(xì)了,但其實(shí)還是很難理解,我因此去請(qǐng)教了一個(gè)前輩高人,他的回答更加驚世駭俗:不修改代碼就可以增加新功能!當(dāng)時(shí)我聽(tīng)到這句話(huà)就震驚了,這是多么神奇的事情啊,不修改代碼就能夠增加新功能!但問(wèn)題是:怎么做到的呢?難道這個(gè)原則是有關(guān)人工智能,又或者有什么高超的技巧,能夠做到不修改代碼增加新功能?這么牛逼的原則當(dāng)然要繼續(xù)探索了,但怎么也沒(méi)有找到“不修改代碼就可以增加新功能”的獨(dú)門(mén)秘籍!于是對(duì)這個(gè)原則有了懷疑,經(jīng)過(guò)繼續(xù)的探索和查看各種資料,才發(fā)現(xiàn)原來(lái)是各位大師們?cè)诮忉屵@個(gè)原則的時(shí)候隱藏了非常重要的“主語(yǔ)”,而這才是OCP原則的關(guān)鍵!大師們省略的主語(yǔ)一個(gè)就是consumer(翻譯成使用者、消費(fèi)者),一個(gè)就是provider(翻譯成生產(chǎn)者、提供著),例如A類(lèi)調(diào)用了B類(lèi)的方法,則A就是consumer,B就是provider。完整的OCP原則實(shí)際上應(yīng)該這樣表述:openforproviderextension,closedforconsumermodification翻譯一下就是:對(duì)使用者修改關(guān)閉,對(duì)提供者擴(kuò)展開(kāi)放!更通俗的意思就是:提供者增加新的功能,但使用者不需要修改代碼!雖然到這里我們已經(jīng)基本上將OCP原則解釋清楚了,但實(shí)際上細(xì)心的朋友還是會(huì)發(fā)現(xiàn)有問(wèn)題的:提供者增加新的功能,使用者不修改代碼就能用上么?比如說(shuō):你設(shè)計(jì)一款有關(guān)車(chē)游戲,需要設(shè)計(jì)一個(gè)“car”的類(lèi),這個(gè)類(lèi)原來(lái)有“加速”、“剎車(chē)”、“轉(zhuǎn)向”三個(gè)功能,現(xiàn)在你要加一個(gè)新功能“改裝”,游戲中其它類(lèi)例如player,不修改代碼就可以用上“改裝”這個(gè)功能么?很顯然這是不可能的,我都新加了一個(gè)函數(shù),你都不調(diào)用就能用新的功能,這也太邪乎了吧?答案在于所謂的增加新功能,并不是增加一個(gè)全新的功能,而是原有的功能有了替代實(shí)現(xiàn),這也是英文的“extension”所隱含的深意!繼續(xù)以賽車(chē)car作為例子,假設(shè)現(xiàn)在你設(shè)計(jì)了“卡車(chē)”、“跑車(chē)”、“家用車(chē)”三種車(chē),現(xiàn)在要增加一種車(chē)“卡丁車(chē)”,只要“卡丁車(chē)”也實(shí)現(xiàn)了“加速”、“剎 車(chē)”、“轉(zhuǎn)向”,那么player不需要修改代碼,就可以玩“卡丁車(chē)”了;但如果你增加了一種“改裝”的功能,那么player必須修改才能使用“改裝” 功能。對(duì)應(yīng)到代碼上來(lái)說(shuō),OCP的應(yīng)用原則如下:1)接口不變:包括函數(shù)名、函數(shù)參數(shù)、函數(shù)返回值等,可以應(yīng)用OCP2)接口改變:已有函數(shù)修改名稱(chēng)、參數(shù)、返回值,或者增加新的函數(shù),OCP都不再適應(yīng)雖然OCP原則是針對(duì)類(lèi)設(shè)計(jì)提出來(lái)的原則,但其思想其實(shí)適應(yīng)很廣,系統(tǒng)和系統(tǒng)、子系統(tǒng)和子系統(tǒng)、模塊和模塊之間都可以應(yīng)用OCP原則,而且不同的地方應(yīng)用其實(shí)都是遵循同一個(gè)原則:通過(guò)接口交互!例如:1)類(lèi)之間應(yīng)用OCP:使用interface進(jìn)行交互;2)模塊和模塊、系統(tǒng)和系統(tǒng):使用規(guī)定好的協(xié)議,不管是私有的還是公開(kāi)的,例如HTTP、SOAP。5. LSP原則 LSP是唯一一個(gè)以人名命名的設(shè)計(jì)原則,而且作者還是一個(gè)“女博士”LSP,Liskovsubstitutionprinciple,中文翻譯為“里氏替換原則”。這是面向?qū)ο笤瓌t中唯一一個(gè)以人名命名的原則,雖然Liskov在中國(guó)的知名度沒(méi)有UNIX的幾位巨匠(KennethThompson、 DennisRitchie)、GOF四人幫那么響亮,但查一下資料,你會(huì)發(fā)現(xiàn)其實(shí)Liskov也是非常牛的:2008年圖靈獎(jiǎng)獲得者,歷史上第一個(gè)女 性計(jì)算機(jī)博士學(xué)位獲得者。其詳細(xì)資料可以在維基百科上查閱:/wiki/Barbara_Liskov言歸正傳,我們來(lái)看看LSP原則到底是怎么一回事。LSP最原始的解釋當(dāng)然來(lái)源于Liskov女士了,她在1987年的OOPSLA大會(huì)上提出了LSP原則,1988年,她將文章發(fā)表在ACM的SIGPLANNotices雜志上,其中詳細(xì)解釋了LSP原則:Atypehierarchyiscomposedofsubtypesandsupertypes.Theintuitiveideaofasubtypeisonewhoseobjectsprovideallthebehaviorofobjectsofanothertype(thesupertype)plussomethingextra.Whatiswantedhereissomethinglikethefollowingsubstitutionproperty:Ifforeachobjecto1oftypeSthereisanobjecto2oftypeTsuchthatforallprogramsPdefinedintermsofT,thebehaviorofPisunchangedwheno1issubstitutedforo2thenSisasubtypeofT.英文比較長(zhǎng),看起來(lái)比較累,我們簡(jiǎn)單的翻譯并歸納一下:1)子類(lèi)的對(duì)象提供了父類(lèi)的所有行為,且加上子類(lèi)額外的一些東西(可以是功能,也可以是屬性);2)當(dāng)程序基于父類(lèi)實(shí)現(xiàn)時(shí),如果將子類(lèi)替換父類(lèi)而程序不需要修改,則說(shuō)明符合LSP原則雖然我們稍微翻譯和整理了一下,但實(shí)際上還是很拗口和難以理解。幸好還有Martin大師也覺(jué)得這個(gè)不怎么通俗易懂,RobertMartin在1996年為C+Reporter寫(xiě)了一篇題為T(mén)heTheLiskovSubstitutionPrinciple的文章,解釋如下:Functionsthatusepointersorreferencestobaseclassesmustbeabletouseobjectsofderivedclasseswithoutknowingit.翻譯一下就是:函數(shù)使用指向父類(lèi)的指針或者引用時(shí),必須能夠在不知道子類(lèi)類(lèi)型的情況下使用子類(lèi)的對(duì)象。Martin 大師解釋了一下,相對(duì)容易理解多了。但Martin大師還不滿(mǎn)足,在2002年,Martin在他出版的 AgileSoftwareDevelopmentPrinciplesPatternsandPractices 一書(shū)中,又進(jìn)一步簡(jiǎn)化為:Subtypesmustbesubstitutablefortheirbasetypes。翻譯一下就是:子類(lèi)必須能替換成它們的父類(lèi)。經(jīng)過(guò)Martin大師的兩次翻譯,我相信LSP原則本身已經(jīng)解釋得比較容易理解了,但問(wèn)題的關(guān)鍵是:如何滿(mǎn)足LSP原則?或者更通俗的講:什么情況下子類(lèi)才能替換父類(lèi)?我們知道,對(duì)于調(diào)用者來(lái)說(shuō)(Liskov解釋中提到的P),和父類(lèi)交互無(wú)非就是兩部分:調(diào)用父類(lèi)的方法、得到父類(lèi)方法的輸出,中間的處理過(guò)程,P是無(wú)法知道的。也就是說(shuō),調(diào)用者和父類(lèi)之間的聯(lián)系體現(xiàn)在兩方面:函數(shù)輸入,函數(shù)輸出。詳細(xì)如下圖:有了這個(gè)圖之后,如何做到LSP原則就清晰了:1)子類(lèi)必須實(shí)現(xiàn)或者繼承父類(lèi)所有的公有函數(shù),否則調(diào)用者調(diào)用了一個(gè)父類(lèi)中有的函數(shù),而子類(lèi)中沒(méi)有,運(yùn)行時(shí)就會(huì)出錯(cuò);2)子類(lèi)每個(gè)函數(shù)的輸入?yún)?shù)必須和父類(lèi)一樣,否則調(diào)用父類(lèi)的代碼不能調(diào)用子類(lèi);3)子類(lèi)每個(gè)函數(shù)的輸出(返回值、修改全局變量、插入數(shù)據(jù)庫(kù)、發(fā)送網(wǎng)絡(luò)數(shù)據(jù)等)必須不比父類(lèi)少,否則基于父類(lèi)的輸出做的處理就沒(méi)法完成。有了這三條原則后,就可以很方便的判斷類(lèi)設(shè)計(jì)是否符合LSP原則了。需要注意的是第3條的關(guān)鍵是“不比父類(lèi)少”,也就是說(shuō)可以比父類(lèi)多,即:父類(lèi)的輸出是子類(lèi)輸出的子集。有的朋友看到這三條原則可能有點(diǎn)納悶:這三條原則一出,那子類(lèi)還有什么區(qū)別哦,豈不都是一樣的實(shí)現(xiàn)了,那還會(huì)有不同的子類(lèi)么?其實(shí)如果仔細(xì)研究這三條原則,就會(huì)發(fā)現(xiàn)其中只是約定了輸入輸出,而并沒(méi)有約束中間的處理過(guò)程。例如:同樣一個(gè)寫(xiě)數(shù)據(jù)庫(kù)的輸出,A類(lèi)可以是讀取XML數(shù)據(jù)然后寫(xiě)入數(shù)據(jù)庫(kù),B類(lèi)可以是從其它數(shù)據(jù)庫(kù)讀取數(shù)據(jù)然后本地的數(shù)據(jù)庫(kù),C類(lèi)可以是通過(guò)分析業(yè)務(wù)日志得到數(shù)據(jù)然后寫(xiě)入數(shù)據(jù)庫(kù)。這3個(gè)類(lèi)的處理過(guò)程都不一樣,但最后都寫(xiě)入數(shù)據(jù)到數(shù)據(jù)庫(kù)了。LSP 原則最經(jīng)典的例子就是“長(zhǎng)方形和正方形”這個(gè)例子。從數(shù)學(xué)的角度來(lái)看,正方形是一種特殊的長(zhǎng)方形,但從面向?qū)ο蟮慕嵌葋?lái)觀察,正方形并不能作為長(zhǎng)方形的一 個(gè)子類(lèi)。原因在于對(duì)于長(zhǎng)方形來(lái)說(shuō),設(shè)定了寬高后,面積=寬*高;但對(duì)于正方形來(lái)說(shuō),設(shè)定高同時(shí)就設(shè)定了寬,設(shè)定寬就同時(shí)設(shè)定了高,最后的面積并不 是等于我們?cè)O(shè)定的寬*高,而是等于最后一次設(shè)定的寬或者高的平方。具體代碼樣例如下:Rectangle.java1. packagecom.oo.java.principles.lsp;2. 3. /*4. *長(zhǎng)方形5. */6. publicclassRectangle7. 8. protectedint_width;9. protectedint_height;10. 11. /*12. *設(shè)定寬13. *paramwidth14. */15. publicvoidsetWidth(intwidth)16. this._width=width;17. 18. 19. /*20. *設(shè)定高21. *paramheight22. */23. publicvoidsetHeight(intheight)24. this._height=height;25. 26. 27. /*28. *獲取面積29. *return30. */31. publicintgetArea()32. returnthis._width*this._height;33. 34. Square.java1. packagecom.oo.java.principles.lsp;2. 3. /*4. *正方形5. */6. publicclassSquareextendsRectangle7. 8. /*9. *設(shè)定“寬”,與長(zhǎng)方形不同的是:設(shè)定了正方形的寬,同時(shí)就設(shè)定了正方形的高10. */11. publicvoidsetWidth(intwidth)12. this._width=width;13. this._height=width;14. 15. 16. /*17. *設(shè)定“高”,與長(zhǎng)方形不同的是:設(shè)定了正方形的高,同時(shí)就設(shè)定了正方形的寬18. */19. publicvoidsetHeight(intheight)20. this._width=height;21. this._height=height;22. 23. 24. UnitTester.java1. packagecom.oo.java.principles.lsp;2. 3. publicclassUnitTester4. 5. publicstaticvoidmain(Stringargs)6. Rectanglerectangle=newRectangle();7. rectangle.setWidth(4);8. rectangle.setHeight(5);9. 10. /如下assert判斷為true11. assert(rectangle.getArea()=20);12. 13. rectangle=newSquare();14. rectangle.setWidth(4);15. rectangle.setHeight(5);16. 17. /如下assert判斷為false,斷言失敗,拋出java.lang.AssertionError18. assert(rectangle.getArea()=20);19. 20. 21.上面這個(gè)樣例同時(shí)也給出了一個(gè)判斷子類(lèi)是否符合LSP的取巧的方法,即:針對(duì)父類(lèi)的單元測(cè)試用例,傳入子類(lèi)是否也能夠測(cè)試通過(guò)。如果測(cè)試能夠通過(guò),則說(shuō)明符合LSP原則,否則就說(shuō)明不符合LSP原則.6. ISP原則 ISP,InterfaceSegregationPrinciple,中文翻譯為“接口隔離原則”。和 DIP原則一樣,ISP原則也是大名鼎鼎的Martin大師提出來(lái)的,他在1996年的C+Reporter發(fā)表 “TheInterfaceSegregationPrinciple”的文章詳細(xì)闡述了ISP原則,并且在他的經(jīng)典著作 AgileSoftwareDevelopment,Principles,Patterns(中文翻譯為:敏捷軟件開(kāi)發(fā):原則、模式與實(shí) 踐)、Practices,an

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論