




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