Java 設(shè)計(jì)模式六大原則
單一職責(zé)原則
定義:不要存在多于一個(gè)導(dǎo)致類(lèi)變更的原因。通俗的說(shuō),即一個(gè)類(lèi)只負(fù)責(zé)一項(xiàng)職責(zé)。
問(wèn)題由來(lái):類(lèi)T負(fù)責(zé)兩個(gè)不同的職責(zé):職責(zé)P1休傍,職責(zé)P2。當(dāng)由于職責(zé)P1需求發(fā)生改變而需要修改類(lèi)T時(shí)尼夺,有可能會(huì)導(dǎo)致原本運(yùn)行正常的職責(zé)P2功能發(fā)生故障。
解決方案:遵循單一職責(zé)原則炒瘸。分別建立兩個(gè)類(lèi)T1淤堵、T2,使T1完成職責(zé)P1功能顷扩,T2完成職責(zé)P2功能拐邪。這樣,當(dāng)修改類(lèi)T1時(shí)隘截,不會(huì)使職責(zé)P2發(fā)生故障風(fēng)險(xiǎn)扎阶;同理,當(dāng)修改T2時(shí)婶芭,也不會(huì)使職責(zé)P1發(fā)生故障風(fēng)險(xiǎn)东臀。
遵循單一職責(zé)原的優(yōu)點(diǎn)有:
- 可以降低類(lèi)的復(fù)雜度,一個(gè)類(lèi)只負(fù)責(zé)一項(xiàng)職責(zé)犀农,其邏輯肯定要比負(fù)責(zé)多項(xiàng)職責(zé)簡(jiǎn)單的多惰赋;
- 提高類(lèi)的可讀性,提高系統(tǒng)的可維護(hù)性呵哨;
- 變更引起的風(fēng)險(xiǎn)降低赁濒,變更是必然的,如果單一職責(zé)原則遵守的好孟害,當(dāng)修改一個(gè)功能時(shí)拒炎,可以顯著降低對(duì)其他功能的影響。
需要說(shuō)明的一點(diǎn)是單一職責(zé)原則不只是面向?qū)ο缶幊趟枷胨赜械陌の瘢灰悄K化的程序設(shè)計(jì)击你,都適用單一職責(zé)原則。
里氏替換原則
定義1:如果對(duì)每一個(gè)類(lèi)型為 T1的對(duì)象 o1谎柄,都有類(lèi)型為 T2 的對(duì)象o2果漾,使得以 T1定義的所有程序 P 在所有的對(duì)象 o1 都代換成 o2 時(shí),程序 P 的行為沒(méi)有發(fā)生變化谷誓,那么類(lèi)型 T2 是類(lèi)型 T1 的子類(lèi)型绒障。
定義2:所有引用基類(lèi)的地方必須能透明地使用其子類(lèi)的對(duì)象。
問(wèn)題由來(lái):有一功能P1捍歪,由類(lèi)A完成』瑁現(xiàn)需要將功能P1進(jìn)行擴(kuò)展鸵钝,擴(kuò)展后的功能為P,其中P由原有功能P1與新功能P2組成庐镐。新功能P由類(lèi)A的子類(lèi)B來(lái)完成恩商,則子類(lèi)B在完成新功能P2的同時(shí),有可能會(huì)導(dǎo)致原有功能P1發(fā)生故障必逆。
解決方案:當(dāng)使用繼承時(shí)怠堪,遵循里氏替換原則。類(lèi)B繼承類(lèi)A時(shí)名眉,除添加新的方法完成新增功能P2外粟矿,盡量不要重寫(xiě)父類(lèi)A的方法,也盡量不要重載父類(lèi)A的方法损拢。
繼承包含這樣一層含義:父類(lèi)中凡是已經(jīng)實(shí)現(xiàn)好的方法(相對(duì)于抽象方法而言)陌粹,實(shí)際上是在設(shè)定一系列的規(guī)范和契約,雖然它不強(qiáng)制要求所有的子類(lèi)必須遵從這些契約福压,但是如果子類(lèi)對(duì)這些非抽象方法任意修改掏秩,就會(huì)對(duì)整個(gè)繼承體系造成破壞。而里氏替換原則就是表達(dá)了這一層含義荆姆。
里氏替換原則通俗的來(lái)講就是:子類(lèi)可以擴(kuò)展父類(lèi)的功能蒙幻,但不能改變父類(lèi)原有的功能。它包含以下4層含義:
- 子類(lèi)可以實(shí)現(xiàn)父類(lèi)的抽象方法胆筒,但不能覆蓋父類(lèi)的非抽象方法杆煞。
- 子類(lèi)中可以增加自己特有的方法。
- 當(dāng)子類(lèi)的方法重載父類(lèi)的方法時(shí)腐泻,方法的前置條件(即方法的形參)要比父類(lèi)方法的輸入?yún)?shù)更寬松决乎。
- 當(dāng)子類(lèi)的方法實(shí)現(xiàn)父類(lèi)的抽象方法時(shí),方法的后置條件(即方法的返回值)要比父類(lèi)更嚴(yán)格派桩。
依賴(lài)倒置原則
定義:高層模塊不應(yīng)該依賴(lài)低層模塊构诚,二者都應(yīng)該依賴(lài)其抽象;抽象不應(yīng)該依賴(lài)細(xì)節(jié)铆惑;細(xì)節(jié)應(yīng)該依賴(lài)抽象范嘱。
問(wèn)題由來(lái):類(lèi)A直接依賴(lài)類(lèi)B,假如要將類(lèi)A改為依賴(lài)類(lèi)C员魏,則必須通過(guò)修改類(lèi)A的代碼來(lái)達(dá)成丑蛤。這種場(chǎng)景下,類(lèi)A一般是高層模塊撕阎,負(fù)責(zé)復(fù)雜的業(yè)務(wù)邏輯受裹;類(lèi)B和類(lèi)C是低層模塊,負(fù)責(zé)基本的原子操作;假如修改類(lèi)A棉饶,會(huì)給程序帶來(lái)不必要的風(fēng)險(xiǎn)厦章。
解決方案:將類(lèi)A修改為依賴(lài)接口I,類(lèi)B和類(lèi)C各自實(shí)現(xiàn)接口I照藻,類(lèi)A通過(guò)接口I間接與類(lèi)B或者類(lèi)C發(fā)生聯(lián)系袜啃,則會(huì)大大降低修改類(lèi)A的幾率。
依賴(lài)倒置原則基于這樣一個(gè)事實(shí):相對(duì)于細(xì)節(jié)的多變性幸缕,抽象的東西要穩(wěn)定的多群发。以抽象為基礎(chǔ)搭建起來(lái)的架構(gòu)比以細(xì)節(jié)為基礎(chǔ)搭建起來(lái)的架構(gòu)要穩(wěn)定的多。在java中发乔,抽象指的是接口或者抽象類(lèi)熟妓,細(xì)節(jié)就是具體的實(shí)現(xiàn)類(lèi),使用接口或者抽象類(lèi)的目的是制定好規(guī)范和契約列疗,而不去涉及任何具體的操作滑蚯,把展現(xiàn)細(xì)節(jié)的任務(wù)交給他們的實(shí)現(xiàn)類(lèi)去完成浪蹂。
在實(shí)際編程中抵栈,我們一般需要做到如下3點(diǎn):
- 低層模塊盡量都要有抽象類(lèi)或接口,或者兩者都有坤次。
- 變量的聲明類(lèi)型盡量是抽象類(lèi)或接口古劲。
- 使用繼承時(shí)遵循里氏替換原則。
依賴(lài)倒置原則的核心就是要我們面向接口編程缰猴,理解了面向接口編程产艾,也就理解了依賴(lài)倒置。
接口隔離原則
定義:客戶(hù)端不應(yīng)該依賴(lài)它不需要的接口滑绒;一個(gè)類(lèi)對(duì)另一個(gè)類(lèi)的依賴(lài)應(yīng)該建立在最小的接口上闷堡。
問(wèn)題由來(lái):類(lèi)A通過(guò)接口I依賴(lài)類(lèi)B,類(lèi)C通過(guò)接口I依賴(lài)類(lèi)D疑故,如果接口I對(duì)于類(lèi)A和類(lèi)B來(lái)說(shuō)不是最小接口杠览,則類(lèi)B和類(lèi)D必須去實(shí)現(xiàn)他們不需要的方法。
解決方案:將臃腫的接口I拆分為獨(dú)立的幾個(gè)接口纵势,類(lèi)A和類(lèi)C分別與他們需要的接口建立依賴(lài)關(guān)系踱阿。也就是采用接口隔離原則。
采用接口隔離原則對(duì)接口進(jìn)行約束時(shí)钦铁,要注意以下幾點(diǎn):
- 接口盡量小软舌,但是要有限度。對(duì)接口進(jìn)行細(xì)化可以提高程序設(shè)計(jì)靈活性是不掙的事實(shí)牛曹,但是如果過(guò)小佛点,則會(huì)造成接口數(shù)量過(guò)多,使設(shè)計(jì)復(fù)雜化黎比。所以一定要適度恋脚。
- 為依賴(lài)接口的類(lèi)定制服務(wù)腺办,只暴露給調(diào)用的類(lèi)它需要的方法,它不需要的方法則隱藏起來(lái)糟描。只有專(zhuān)注地為一個(gè)模塊提供定制服務(wù)怀喉,才能建立最小的依賴(lài)關(guān)系。
- 提高內(nèi)聚船响,減少對(duì)外交互躬拢。使接口用最少的方法去完成最多的事情。
運(yùn)用接口隔離原則见间,一定要適度聊闯,接口設(shè)計(jì)的過(guò)大或過(guò)小都不好。設(shè)計(jì)接口的時(shí)候米诉,只有多花些時(shí)間去思考和籌劃菱蔬,才能準(zhǔn)確地實(shí)踐這一原則。
迪米特原則
定義:一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象保持最少的了解史侣。
問(wèn)題由來(lái):類(lèi)與類(lèi)之間的關(guān)系越密切拴泌,耦合度越大,當(dāng)一個(gè)類(lèi)發(fā)生改變時(shí)惊橱,對(duì)另一個(gè)類(lèi)的影響也越大蚪腐。
解決方案:盡量降低類(lèi)與類(lèi)之間的耦合。
開(kāi)閉原則
定義:一個(gè)軟件實(shí)體如類(lèi)税朴、模塊和函數(shù)應(yīng)該對(duì)擴(kuò)展開(kāi)放回季,對(duì)修改關(guān)閉。
問(wèn)題由來(lái):在軟件的生命周期內(nèi)正林,因?yàn)樽兓菀弧⑸?jí)和維護(hù)等原因需要對(duì)軟件原有代碼進(jìn)行修改時(shí),可能會(huì)給舊代碼中引入錯(cuò)誤觅廓,也可能會(huì)使我們不得不對(duì)整個(gè)功能進(jìn)行重構(gòu)鼻忠,并且需要原有代碼經(jīng)過(guò)重新測(cè)試。
解決方案:當(dāng)軟件需要變化時(shí)哪亿,盡量通過(guò)擴(kuò)展軟件實(shí)體的行為來(lái)實(shí)現(xiàn)變化粥烁,而不是通過(guò)修改已有的代碼來(lái)實(shí)現(xiàn)變化。
單一職責(zé)原則告訴我們實(shí)現(xiàn)類(lèi)要職責(zé)單一蝇棉;里氏替換原則告訴我們不要破壞繼承體系讨阻;依賴(lài)倒置原則告訴我們要面向接口編程;接口隔離原則告訴我們?cè)谠O(shè)計(jì)接口的時(shí)候要精簡(jiǎn)單一篡殷;迪米特法則告訴我們要降低耦合钝吮。而開(kāi)閉原則是總綱,他告訴我們要對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉奇瘦。