日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

设计模式六大原则

發布時間:2023/11/16 windows 85 coder
生活随笔 收集整理的這篇文章主要介紹了 设计模式六大原则 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

什么是設計模式?

設計模式是軟件設計人員、軟件開發人員在程序代碼編寫中總結出來的一套編碼規范,設計模式起一個指導作用,用來指導我們寫出高內聚低耦合,具有良好的可擴展性和可維護性的代碼。

為什么要學設計模式?

當然,設計模式不是非學不可,不了解設計模式一樣可以在工作中寫出符合產品要求的功能。但是隨著功能的不斷迭代,需求不斷增加和變更,項目中的代碼會不斷在在原有功能代碼的基礎之上堆疊,最終會形成難以維護的一坨屎山。另外,作為程序員,寫出好的代碼是我們基本的追求,也可以從專業的角度提升自己。

設計模式怎么學?

設計模式有非常多種,作為一個程序員,在日常寫代碼的過程中肯定有意無意的用到過某些模式。現在我們知道的23種設計模式,都是前輩們在各種實際開發場景中總結提取出來的,是一個通用的解決方案。雖說有23種之多,但這些模式都遵循了6大原則,了解了這6大原則再去看具體的設計模式就很容易理解了。

設計模式六大原則

單一職責原則

一個類只能有一個可以引起它變化的原因

說白了就是一個類只做一件事。那我們為什么需要單一職責?如果某個類A承擔了多個職責A1,A2,A3,因為某些原因需要對這三個任何一個職責進行修改或變更都可能會影響到其他職責,可能導致發生故障。所以最好的做法是將A拆分成三個類,每個類只負責一個職責。
合理的遵循單一職責原則,可以提高類的可讀性、可維護性,降低類復雜度,從而提升了系統的可維護性。但是在我們日常工作中,在各種或者復雜或者簡單的業務需求背景下,如何確定一個類的職責范圍就需要我們好好思考了。

開閉原則

軟件(類、模塊、函數等)應該要開放擴展,但是不能支持修改,即對修改關閉,對新增開放

我們在做任何系統的時候,都不能指望系統一開始時需求確定,就再也不會變化,這是不現實也不科學的想法,而既然需求是一定會變化的,那么如何在面對需求的變化時,設計的軟件可以相對容易修改,不至于說新需求一來,就要把整個程序推倒重來。怎樣的設計才能面對需求的改變卻可以保持相對穩定,并且預留好一些可擴展的點,從而使得系統可以在基于第一個版本以后不斷擴展處新功能?我們用一個例子來說明怎樣對擴展開放,對修改關閉。
假設我們有一個廚師類,該類有一些成員變量和一個方法:炒菜,這個方法接收一個菜名,并且方法內部根據菜名進行不同的制作,具體類如下。

class 廚師 {
    int 年齡;
    string 姓名;
    string 身份證;
    
    public void 炒菜(string 菜名){
        ...
        洗鍋、洗菜、準備調味料等
        ...
        if(菜名=="西紅柿炒雞蛋"){
          ...
          炒西紅柿邏輯;
          ...
          炒雞蛋邏輯;
          ...    
          其他邏輯繼續追加
          ...
        } else if (菜名=="酸辣土豆絲"){
          ...
        }
    }
}

如果有一天廚師突發靈感,想要對西紅柿炒雞蛋的制作工藝進行改良,那么應該怎么做?按照當前的做法是直接在炒菜這個方法內,對if塊的邏輯進行邏輯修改,在足夠細心的情況下修改此邏輯或許沒什么問題,但在一個團隊內,開發人員的風格和習慣各不相同,很難保證每個人在修改完西紅柿炒雞蛋的邏輯后可以不影響其他邏輯,此時就需要一個代碼層面上的規范來強制約束大家,必須按照某個規范修改邏輯,并且這個規范天然不會影響其他邏輯,這個規范就是設計模式,在當前場景就是單一職責原則開閉原則
單一職責原則和開閉原則在這種場景下要求我們,需要將各種菜的邏輯單獨拆分出來,并且將廚師制作的邏輯進行抽象。拆分后的邏輯類如下。

abstract class 菜譜{
    string 菜名;
    public abstract void 準備配料();
    public abstract void 制作();
}

class 西紅柿炒雞蛋 extend 菜譜{
    string 菜名;
    
    public void 準備配料(){
        ...
        準備鹽醋醬油
        ...
    }
    
    public void 制作(){
        ...
        炒西紅柿邏輯;
        ...
        炒雞蛋邏輯;
        ...    
    }
}

class 酸辣土豆絲 extend 菜譜{
  string 菜名;
  
  public void 準備配料(){
        ...
    }
    
    public void 制作(){
        ...
    }
}

class 廚師{
    int 年齡;
    string 姓名;
    string 身份證;
   
   public void 炒(菜譜 菜譜){
       菜譜.準備配料();
       菜譜.制作();
   }
}

在上面的例子中,我們定義了一個菜譜基類,并且將所有菜的制作邏輯單獨創建類并且繼承菜譜類,實現制作一道菜的準備配料制作邏輯。廚師則不局限于具體某道菜,而是根據菜譜炒菜。這樣,即使要對西紅柿炒雞蛋的制作邏輯進行改良,也不會影響到其他菜的邏輯。并且以后如果引進新菜譜,廚師也可以直接按照新菜譜進行制作,這樣就遵循了對修改關閉,對新增開放的原則了。

依賴倒置原則

高層模塊不應該依賴底層模塊,而應該依賴抽象;抽象不應該依賴細節,細節應該依賴抽象。

我們首先看例子,然后再解釋這句話。

class 西紅柿炒雞蛋{
    string 菜名;
    
    public void 準備配料(){
        ...
        準備鹽醋醬油
        ...
    }
    
    public void 制作(){
        ...
        炒西紅柿邏輯;
        ...
        炒雞蛋邏輯;
        ...    
    }
}

class 廚師{
    int 年齡;
    string 姓名;
    string 身份證;
   
   public void 西紅柿炒雞蛋(){
       西紅柿炒雞蛋 菜 = new 西紅柿炒雞蛋();
       菜.準備配料();
       菜.制作();
   }
}

在上面的例子中,廚師類就是高層模塊,而西紅柿炒雞蛋酸辣土豆絲屬于底層模塊,此時的廚師類依賴了底層的實現。假如西紅柿炒雞蛋這道菜增加了一個邏輯剝西紅柿皮,那么廚師類也要增加調用方法,這樣就減小了系統的可維護性。我們來看看修改后的邏輯實現。

abstract class 菜譜{
    string 菜名;
    public abstract void 制作();
}

class 西紅柿炒雞蛋 extend 菜譜{
    string 菜名;
    
    private void 剝西紅柿皮(){
      ...
    }
    
    private void 準備配料(){
        ...
        準備鹽醋醬油
        ...
    }
    
    private void 炒西紅柿(){
    }
    
    private void 炒雞蛋(){
    }
    
    public void 制作(){
        剝西紅柿皮();
        準備配料();
        炒雞蛋();
        炒西紅柿();
    }
}

class 廚師{
    int 年齡;
    string 姓名;
    string 身份證;
   
   public void 炒(菜譜 菜譜){
       菜譜.制作();
   }
}

修改后的邏輯,廚師類只依賴于抽象類菜譜的抽象方法制作,此時高層模塊廚師沒有直接依賴具體實現,而是依賴了菜譜這個抽象類。具體的菜西紅柿炒雞蛋要怎么炒、哪塊需要增加制作步驟,全部在西紅柿炒雞蛋的菜譜中進行修改。
如何理解“抽象不應該依賴細節,細節應該依賴抽象”這句話?我們有一個菜譜抽象類和西紅柿炒雞蛋實現類,此時如果西紅柿炒雞蛋要增加步驟剝西紅柿皮,如果在抽象類中增加方法剝皮,并在西紅柿炒雞蛋類中將剝西紅柿皮的實現邏輯寫在剝皮方法中,就犯了抽象依賴細節的錯誤了。抽象類應該是從具有某一同一行為的一類活動中抽象出來的通用類,而在本例中,同一行為就是菜的制作,而對于西紅柿炒雞蛋的所有制作過程,都屬于制作。所以在抽象類中提供了制作方法后,實現類西紅柿炒雞蛋的所有制作邏輯都應該在制作方法中實現,而非在抽象類中增加方法并在子類實現,這個就是細節應該依賴抽象

里氏替換原則

任何基類可以出現的地方,子類一定可以出現。

里氏替換原則要求我們在所有依賴父類的地方,子類可以完全替代父類并且對邏輯無影響。在子類重寫了父類已實現邏輯的情況下很容易違反此原則,我們還是看具體栗子。

abstract class 廚師{
    
    abstract void 洗菜();
    
    abstract void 調味();
    
    void 炒(){
       洗菜();
       ...
       下鍋邏輯
       ...
       調味();
       ...
       出鍋邏輯
       ...
    }
}

class 張三 extend 廚師{
    
    string 洗菜(){
        ...
        洗菜邏輯
        ...
    }
    
    string 調味(){
        ...
        調味邏輯
        ...
    }
    //這里覆蓋了父類的已實現方法
    void 炒(){
        ...
        下鍋邏輯
        ...
        調味();
        ...
        出鍋邏輯
        ...
        洗菜();
    }
}

class 飯店{
    void 炒菜(){
        廚師 張三 = new 張三();
        張三.炒();
    }
}

抽象類廚師類作為父類,定義了兩個抽象方法和一個已實現方法。子類張三繼承了廚師類,并實現了兩個抽象方法:洗菜調味,并且又重寫了父類已實現的方法,此時父類的方法和子類的邏輯就不一致。在父類方法中,邏輯流程是“洗菜-下鍋-調味-出鍋”,意味著子類所有的邏輯都必須按照這個流程執行。但子類張三重寫的邏輯時下鍋-調味-出鍋-洗菜,邏輯不同,就不能在父類出現的地方替換成子類,否則可能會造成系統或者流程異常。

迪米特法則

一個對象應該對其他對象保持最少的了解,又叫最少知道原則。

在類的結構設計上,每個類都應當盡量降低成員的訪問權限,不需要讓別的類知道的字段或行為就不公開,否則會破壞類的預期行為和安全性,我們直接看例子。

class 西紅柿炒雞蛋{
    
    private int 雞蛋;
    private int 西紅柿;
    private int 鹽;
    private int 醋;

    public 西紅柿炒雞蛋(int 雞蛋,int 西紅柿,int 鹽,int 醋){
        this.雞蛋=雞蛋;
        this.西紅柿=西紅柿;
        this.鹽=鹽;
        this.醋=醋;
    }
    
    public void 制作(){
        ...
        攪拌雞蛋(this.雞蛋);
        ...
        切西紅柿(this.西紅柿);
        ...
        加入鹽(this.鹽);
        ...
        加入醋(this.醋);
        ...
    }
}

class 廚師{
    
    public void 炒(){
        西紅柿炒雞蛋 菜=new 西紅柿炒雞蛋(2,1,500克,1升);
        菜.炒();
    }
}

拋開前面講的幾個原則先不管,第一眼看上面的例子好像沒什么問題,廚師類有方法,西紅柿雞蛋類也沒其他無關邏輯,但我們看實例化西紅柿炒雞蛋的代碼,實例化時傳入的雞蛋數2、西紅柿1、鹽500克、醋1升,看出問題了吧。誰家炒兩個雞蛋要放500克鹽1升醋,這樣做出來的菜還能吃嗎?所以很明顯這個實例化時的入參是有問題的,鹽和醋作為西紅柿炒雞蛋這道菜中的關鍵參數,需要用多少應該是根據雞蛋和西紅柿的數量來確定的,而不是初始化時任意傳入的。所以這個類的定義就違反了最少知道原則,將關鍵參數通過構造函數暴漏出來了。

class 西紅柿炒雞蛋{
    
    private int 雞蛋;
    private int 西紅柿;

    public 西紅柿炒雞蛋(int 雞蛋,int 西紅柿){
        this.雞蛋=雞蛋;
        this.西紅柿=西紅柿;
    }
    
    public void 制作(){
        ...
        攪拌雞蛋(this.雞蛋);
        ...
        切西紅柿(this.西紅柿);
        int 鹽=0;
        int 醋=0;
        
        if(雞蛋==2 && 西紅柿==1){
            鹽=10;
            醋=10;
        }else if(/*其他判斷邏輯*/){
            
        }
    }
}

class 廚師{
    
    public void 炒(){
        西紅柿炒雞蛋 菜=new 西紅柿炒雞蛋(2,1);
        菜.制作();
    }
}

上面我們修改過后的類定義,西紅柿炒雞蛋構造函數只接受雞蛋西紅柿數量,而關鍵參數則是在正式制作的時候,根據雞蛋和西紅柿的數量來最終確定,這樣,無論要炒多少個雞蛋和西紅柿都會有對應的被放入,確保炒出來的菜是真正可以吃的,即我們定義的類的行為是符合預期的。

接口隔離原則

使用多個專門的接口,而不使用單一的總接口,即客戶端不應該依賴那些它不需要的接口。

我們直接看示例

abstract class 人{
    abstract void 吃飯();
    
    abstract void 睡覺();
    
    abstract void 跑步();
    
    abstract void 工作();
    
   abstract void 爬();
}

class 嬰兒 extends 人{
    void 吃飯(){
    }
    void 睡覺(){
    }
    
    void 跑步(){
        //沒法跑
    }
    void 工作(){
        //沒法工作
    }
    void 爬(){
    }
}

class 成人 extends 人{
    void 吃飯(){
    }
    void 睡覺(){
    }
    void 跑步(){
    }
    void 工作(){
    }
    void 爬(){
        //沒必要
    }
}

在上面的代碼中,我們定義了一個抽象類,并且定義了5個抽象方法。有兩個子類嬰兒成人,分別實現了抽象類定義的5個方法,但我們注意到,在嬰兒子類中是沒法實現跑步工作邏輯的,因為嬰兒不具備這樣的能力。而在成人子類中,也沒必要實現的方法,因為成人沒必要爬。此時雖然在基類中定義的所有行為都是屬于人的,但并非所有繼承自的子類都需要全部實現這些方法,此時就違背了接口隔離原則。那么我們看看如何修改基類定義。

abstract class 人{
    abstract void 吃飯();
    
    abstract void 睡覺();
}

abstract class 嬰兒 extends 人{
    abstract void 爬();
}

abstract class 成人 extends 人{
    abstract void 跑步();
    
    abstract void 工作();
}

class 張三 extends 成人{
    void 吃飯(){
    }
    void 睡覺(){
    }
    void 跑(){
    }
    void 工作(){
    }
}

class 寶寶 extends 嬰兒{
    void 吃飯(){
    }
    void 睡覺(){
    }
    void 爬(){
    }
}

在上面修改后的代碼中,抽象類只定義了兩個抽象方法吃飯睡覺,繼承自的兩個子類抽象類嬰兒成人分別定義各自的抽象方法跑步工作。那么在具體的實現類中,我們就可以繼承不同的類:張三作為一個成人擁有基本的吃飯、睡覺、跑、工作行為,而寶寶作為嬰兒則有吃飯、睡覺、爬的行為。這樣各個類根據各自需求,繼承滿足要求的單一接口,而不用繼承一個大而全但其中的許多行為都沒法實現的接口,也避免了在依賴方調用對應對象方法時,某些行為未實現導致的功能異常。

總結

設計模式可以指導我們代碼的結構搭建,而這六大原則則指明了設計模式的基本遵循的準則,在我們日常編寫代碼的時候,如果能比較好的遵循這些原則,那么即便我們沒有按照某個具體的模式套在對應的場景上,寫出來的代碼也會具有較好的可維護性。

總結

以上是生活随笔為你收集整理的设计模式六大原则的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。