架構(gòu)中的設(shè)計(jì)原則

架構(gòu)中的設(shè)計(jì)原則

在使用面向?qū)ο蟮乃枷脒M(jìn)行系統(tǒng)設(shè)計(jì)時(shí),前人共總結(jié)出了7條原則:?jiǎn)我宦氊?zé)原則、開(kāi)閉原則、里氏替換原則、依賴注入原則、接口分離原則、迪米特原則和優(yōu)先使用組合而不是繼承原則。

1. 單一原則

核心思想:系統(tǒng)中的每一個(gè)對(duì)象都應(yīng)該只有一個(gè)單獨(dú)的職責(zé),而所有的對(duì)象所關(guān)注的就是自身職責(zé)的完成。Single Responsibility Principle

每個(gè)類應(yīng)該只有一個(gè)職責(zé),對(duì)外只能提供一種功能,而引起類變化的原因應(yīng)該只有一個(gè)。在設(shè)計(jì)模式中,所有的設(shè)計(jì)模式都遵循這一原則。

通常,一個(gè)類的“職責(zé)”越多,導(dǎo)致其變化的因素也就越多。因?yàn)槊總€(gè)職責(zé)都可能是一個(gè)變化的軸線。一般,我們?cè)谠O(shè)計(jì)一個(gè)類的時(shí)候,會(huì)把與該類有關(guān)的操作都組合到這個(gè)類中,這樣設(shè)計(jì)的后果就有可能將多個(gè)職責(zé)“耦合”到了一塊,當(dāng)這個(gè)類的某個(gè)職責(zé)發(fā)生變化時(shí),很難避免其他的部分不受影響。

解決這種問(wèn)題的方法就是“分藕”,將不同的職責(zé)分別進(jìn)行封裝,不要將其組合在一個(gè)類中。比如使用多個(gè)接口定義業(yè)務(wù)操作,每個(gè)接口所定義的業(yè)務(wù)都是單一的。

5點(diǎn)注意:

  1. 一個(gè)合理的類,應(yīng)該僅有一個(gè)引起它變化的原因,即單一原則;
  2. 在沒(méi)有變化征兆的情況下使用SRP或其他原則是不明智的;
  3. 在需求實(shí)際發(fā)生變化時(shí)就應(yīng)該應(yīng)用SRP等原則來(lái)重構(gòu)代碼;
  4. 使用測(cè)試驅(qū)動(dòng)開(kāi)發(fā)會(huì)迫使我們?cè)谠O(shè)計(jì)出劣質(zhì)代碼之前就分理出不合理代碼;
  5. 如果測(cè)試不能迫使職責(zé)分離,僵化性和脆弱性的腐朽味會(huì)變得很濃烈,那就應(yīng)該使用Facade(外觀)或Proxy(代理)模式對(duì)代碼重構(gòu);

2. 里氏替換原則

核心思想:在任何父類出現(xiàn)的地方都可以用它的子類來(lái)替代Liskov Subsitution Principle

同一個(gè)集成體系中的對(duì)象應(yīng)該有共同的行為特征。里氏替換原則關(guān)注的是怎樣良好地使用繼承,也就是說(shuō)不要亂用繼承,它是繼承復(fù)用的基石。只要父類出現(xiàn)的地方,子類就能出現(xiàn),而且替換為子類不會(huì)產(chǎn)生任何錯(cuò)誤或異常。反過(guò)來(lái)可能就出現(xiàn)問(wèn)題了。

4層含義:

  1. 子類必須完全實(shí)現(xiàn)父類的方法;
  2. 子類可以用擁有自己的特性;
  3. 覆蓋或者實(shí)現(xiàn)父類的方法時(shí)輸入?yún)?shù)可以被放大;(結(jié)合重載考慮父類方法參數(shù)是HashMap而子類方法參數(shù)是Map)
  4. 覆蓋或者實(shí)現(xiàn)父類的方法時(shí)輸出結(jié)果可以被縮小;
    父類能出現(xiàn)的地方子類就可以出現(xiàn),而且替換為子類不會(huì)產(chǎn)生任務(wù)錯(cuò)誤或者異常,使用者也無(wú)需知道是父類還是子類,但是反過(guò)來(lái)就不行了。

3. 依賴注入原則

核心思想:要依賴抽象,不要依賴于具體的實(shí)現(xiàn),Dependence Inversion Principle(也可以翻譯為依賴反轉(zhuǎn)原則)

在應(yīng)用程序中,所有的類如果使用或依賴于其他的類,則都應(yīng)該依賴于這些類的抽象類,而不是這些類的具體實(shí)現(xiàn)類。抽象層次應(yīng)該不依賴于具體的實(shí)現(xiàn)細(xì)節(jié),這樣才能保證系統(tǒng)的可復(fù)用性和可維護(hù)性。就要要求開(kāi)發(fā)人員面向接口編程而非針對(duì)實(shí)現(xiàn)編程。

3點(diǎn)說(shuō)明:

  1. 高層模塊不應(yīng)該依賴低層模塊,兩者都應(yīng)該依賴于抽象(抽象類或接口);
  2. 抽象(抽象類或接口)不應(yīng)該依賴于細(xì)節(jié)(具體實(shí)現(xiàn)類);
  3. 細(xì)節(jié)(具體實(shí)現(xiàn)類)應(yīng)該依賴抽象;

本質(zhì)是通過(guò)抽象(抽象類或接口)使各個(gè)類或模塊的實(shí)例彼此獨(dú)立,互不影響,實(shí)現(xiàn)模塊間的松耦合。這個(gè)原則也是6個(gè)原則中最難以實(shí)現(xiàn)的,如果沒(méi)有實(shí)現(xiàn)這個(gè)原則,那么意味著開(kāi)閉原則(對(duì)擴(kuò)展開(kāi)發(fā),對(duì)修改關(guān)閉)也無(wú)法實(shí)現(xiàn)。

3種實(shí)現(xiàn)方式:

  1. 通過(guò)構(gòu)造函數(shù)傳遞依賴對(duì)象; 構(gòu)造函數(shù)中需要傳遞的參數(shù)是抽象類或接口的方式實(shí)現(xiàn);
  2. 通過(guò)setter方法傳遞依賴對(duì)象;我們?cè)O(shè)置的set方法中,參數(shù)為抽象類或接口,來(lái)實(shí)現(xiàn)傳遞依賴對(duì)象;
  3. 接口聲明實(shí)現(xiàn)依賴對(duì)象
public interface IFood{
   public void eat(); 
}
public class Noodle implements IFood{
    @Override
    public void eat(){
        System.out.println("吃面條~~");
    }
}

public class Rice implements IFood{
    @Override
    public void eat(){
        System.out.println("吃米飯~~");
    }
}

...
public interface Man{
    public void cook(IFood food);
}

public class Cooker implements Man{
    @Override
    public void cook(IFood food){
        food.eat();
    }
}
public class App{
    public static void main(String [] args){
        Cooker cooker = new Cooker();
        IFood noodel = new Noodle();
        //cooker.cook(noodel);
        IFood rice = new Rice();
        //cooker.cook(rice);
        ...
    }
}

這樣各個(gè)類或模塊的實(shí)現(xiàn)彼此獨(dú)立,互補(bǔ)影響,實(shí)現(xiàn)了模塊間的松耦合。

4. 接口分離原則

核心思想:不應(yīng)該強(qiáng)迫客戶程序依賴他們不需要使用的方法,Interface Segregation Principle,一個(gè)不需要提供太多的行為,一個(gè)接口應(yīng)該只提供一種對(duì)外的功能,不應(yīng)該把所有的操作都封裝在一個(gè)接口中。

接口分離原則要求的是在一個(gè)模塊中應(yīng)該只依賴它需要的接口,以保證接口小純潔,而且要保證接口應(yīng)該盡量小。

接口分離原則與單一職責(zé)原則有點(diǎn)類似,不過(guò)不同在于:?jiǎn)我宦氊?zé)原則要求的是類和接口職責(zé)單一,注重是職責(zé),業(yè)務(wù)邏輯的劃分。而接口分離原則要求的是接口的方法盡量少,針對(duì)單一模塊盡量有用。

3點(diǎn)規(guī)范:

  1. 接口盡量?。簽榱吮WC一個(gè)接口只服務(wù)于一個(gè)子模塊或者業(yè)務(wù)邏輯;
  2. 接口高內(nèi)聚:接口高內(nèi)聚是對(duì)內(nèi)高度依賴,對(duì)外盡可能隔離。即一個(gè)接口內(nèi)部聲明的方法相互之間都與某一個(gè)子模塊相關(guān),且是這個(gè)模塊必需的;
  3. 接口設(shè)計(jì)是有限度的,65535;

5. 迪米特原則

核心思想:一個(gè)對(duì)象應(yīng)當(dāng)對(duì)其他對(duì)象盡可能少地了解,降低各個(gè)對(duì)象之間的耦合,提高系統(tǒng)的可維護(hù)性。在模塊間,應(yīng)該只通過(guò)接口來(lái)通信,而不理會(huì)模塊的內(nèi)部工作原理,促進(jìn)軟件的復(fù)用。

7點(diǎn)注意事項(xiàng):

  1. 在類的劃分上,應(yīng)該創(chuàng)建有弱耦合的類;
  2. 在類的結(jié)構(gòu)設(shè)計(jì)上,每一個(gè)都應(yīng)當(dāng)盡量降低成員的訪問(wèn)權(quán)限;
  3. 在類的設(shè)計(jì)上,只要有可能,一個(gè)類應(yīng)當(dāng)設(shè)計(jì)成不可變類;
  4. 在對(duì)其他類的引用上,一個(gè)對(duì)象對(duì)其他對(duì)象的引用應(yīng)當(dāng)降到最低;
  5. 盡量降低類的訪問(wèn)權(quán)限;
  6. 謹(jǐn)慎使用序列化功能;
  7. 不要暴露類成員,而應(yīng)該提供相應(yīng)的訪問(wèn)器(屬性);

6. 開(kāi)閉原則

核心思想:一個(gè)對(duì)象對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉Open for Extension,Closed for Modification。

意思對(duì)類的改動(dòng)通過(guò)增加代碼進(jìn)行的,而不是改動(dòng)現(xiàn)有的代碼。開(kāi)發(fā)人員一旦寫(xiě)出了可行的代碼,就不應(yīng)該去改變它,而是要保證它能一直運(yùn)行下去。這就要借助抽象和多態(tài),把可能變化的內(nèi)容抽象出來(lái),從而抽象出來(lái)的部分是相對(duì)穩(wěn)定的,而具體的實(shí)現(xiàn)層是可以改變和擴(kuò)展的。

開(kāi)閉原則是前5種原則的一個(gè)抽象總結(jié),前5種是開(kāi)閉原則的一些具體體現(xiàn)。所以如果使用開(kāi)閉原則,其實(shí)有點(diǎn)虛,因?yàn)樗鼪](méi)有一個(gè)固定的模式,但是最終保證的是提高程序的復(fù)用性、可維護(hù)性等要求。

寫(xiě)在最后:
不過(guò)這些設(shè)計(jì)原則并不是絕對(duì)的,而是根據(jù)項(xiàng)目的實(shí)際需求來(lái)定奪。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 關(guān)于設(shè)計(jì)模式六大設(shè)計(jì)原則的資料網(wǎng)上很多,但感覺(jué)很多地方解釋地都太過(guò)于籠統(tǒng)化,特此再總結(jié)一波。 優(yōu)化第一步-單...
    ghroost閱讀 1,260評(píng)論 0 5
  • 程序設(shè)計(jì)的6大原則: 單一職責(zé)原則里氏替換原則依賴倒置原則接口隔離原則迪米特法則開(kāi)閉原則 從根本學(xué)好,理解為什么要...
    silencefun閱讀 2,474評(píng)論 1 4
  • 單一職責(zé)原則 (SRP) 全稱 SRP , Single Responsibility Principle 單一職...
    米莉_L閱讀 1,865評(píng)論 2 5
  • 軟件開(kāi)發(fā)是始于面向過(guò)程的 軟件開(kāi)發(fā)是始于面向過(guò)程的,因?yàn)槊嫦蜻^(guò)程地解決問(wèn)題更直接,軟件本身就是一個(gè)解決問(wèn)題的過(guò)程;...
    侏羅紀(jì)猿閱讀 821評(píng)論 0 2
  • 參考書(shū)籍 JavaScript高級(jí)程序設(shè)計(jì) JavaScript語(yǔ)言精粹 ECMAScript 6 入門 一些注意...
    blossom_綻放閱讀 316評(píng)論 0 0

友情鏈接更多精彩內(nèi)容