設(shè)計模式之面向?qū)ο笃叽笤瓌t

1. 單一職責(zé)原則(Single Responsibility Principle)
2. 里氏替換原則(Liskov Substitution Principle)
3. 依賴倒置原則(Dependence Inversion Principle)
4. 接口隔離原則(Interface Segregation Principle)
5. 迪米特法則(Law Of Demeter)
6. 開閉原則(Open Close Principle)
7. 組合/聚合復(fù)用原則(Composite/Aggregate Reuse Principle CARP)


縮寫 原則名稱 概述
SPR 單一職責(zé)原則 每一個類應(yīng)該專注于做一件事情。
LSP 里氏替換原則 基類存在的地方,子類是可以替換的
DIP 依賴倒置原則 實現(xiàn)盡量依賴抽象镜雨,不依賴具體實現(xiàn)。高層模塊不應(yīng)該直接依賴于低層模塊友浸,高層模塊和低層模塊應(yīng)該同時依賴一個抽象層桑驱。
ISP 接口隔離原則 應(yīng)當為客戶端提供盡可能小的單獨的接口,而不是提供大的總的接口脉顿。
LOD 迪米特法則 又叫最少知識原則,一個軟件實體應(yīng)當盡可能少的與其他實體發(fā)生相互作用点寥。
OCP 開閉原則 面向擴展開放艾疟,面向修改關(guān)閉。
CARP 組合/聚合復(fù)用原則 盡量使用合成/聚合達到復(fù)用,少用繼承蔽莱。原則: 一個類中有另一個類的對象弟疆。

解析

[1]

1. 單一職責(zé)原則(Single Responsibility Principle):
見名知意无切,這個條職責(zé)的潛臺詞的就是累贤,專注做一個事规伐。單一職責(zé)原則可以降低類的復(fù)雜度米诉,一個類只負責(zé)一項職責(zé)峡碉,其邏輯肯定要比負責(zé)多項職責(zé)簡單的多找前;提高類的可讀性踊餐,提高系統(tǒng)的可維護性拷呆;變更引起的風(fēng)險降低锅劝,變更是必然的攒驰,如果遵守的好,當修改一個功能時故爵,可以顯著降低對其他功能的影響玻粪。需要說明的一點是單一職責(zé)原則不只是面向?qū)ο缶幊趟枷胨赜械模灰悄K化的程序設(shè)計诬垂,都適用此原則劲室。


[2]

2. 里氏替換原則(Liskov Substitution Principle):
將一個基類對象替換成它的子類對象,程序?qū)⒉粫a(chǎn)生任何錯誤和異常结窘。反之則不成立很洋,因為如果使用的是一個子類對象的話,那么它不一定能夠使用基類對象(子類擁有父類未擁有的函數(shù))晦鞋。
此原則是實現(xiàn)開閉原則的重要方式之一蹲缠,由于使用基類對象的地方都可以使用子類對象,因此在程序中盡量使用基類類型來定義對象悠垛,而在運行時再確定其子類類型线定,用子類對象來替換父類對象。
使用里氏替換原則時需要注意确买,子類的所有方法必須在父類中聲明斤讥,或子類必須實現(xiàn)父類中聲明的所有方法。盡量把父類設(shè)計為抽象類或者接口湾趾,讓子類繼承父類或?qū)崿F(xiàn)父接口芭商,并實現(xiàn)在父類中聲明的方法,運行時搀缠,子類實例替換父類實例铛楣,我們可以很方便地擴展系統(tǒng)的功能,同時無須修改原有子類的代碼艺普,增加新的功能可以通過增加新的子類來實現(xiàn)簸州。從大局看多態(tài)就屬于這個原則鉴竭。

示例代碼

public abstract class  Phone
{
    public abstract void Call();
}
 interface Android{ }
 interface IOS{ }
public class OnePlus : Phone, Android
{
    public override void Call()
    {
        Debug.Log($"{nameof(OnePlus)}進行通話。岸浑。搏存。。矢洲。");
    }
}

public class Pixel : Phone, Android
{
    public override void Call()
    {
        Debug.Log($"{nameof(Pixel)}進行通話璧眠。。读虏。责静。。");
    }
}
public class XiaoMi : Phone, Android
{
    public override void Call()
    {
        Debug.Log($"{nameof(XiaoMi)}進行通話掘譬。泰演。。葱轩。。");
    }
}

public class Apple : Phone, IOS
{
    public override void Call()
    {
        Debug.Log($"{nameof(Apple)}進行通話藐握。靴拱。。猾普。袜炕。");
    }
}

不使用里氏替換,調(diào)用call函數(shù)需要為每個類型的手機寫一個函數(shù)

    public void WantToCall_0(OnePlus phone)
    {
        phone.Call();
    }

    public void WantToCall_1(Pixel phone)
    {
        phone.Call();
    }

    public void WantToCall_2(XiaoMi phone)
    {
        phone.Call();
    }

    public void WantToCall_3(Apple phone)
    {
        phone.Call();
    }

使用里氏替換初家,只需要一個函數(shù)全部搞定偎窘,在后面的【橋接模式】會廣泛用到的

    public void WantToCall_4(Phone phone)
    {
        phone.Call();
    }

[3]

3. 依賴倒置原則(Dependence Inversion Principle):
高層模塊不應(yīng)該依賴低層模塊,二者都應(yīng)該依賴其抽象溜在;抽象不應(yīng)該依賴細節(jié)陌知;細節(jié)應(yīng)該依賴抽象。 依賴倒置原則的核心思想是面向接口編程掖肋。
采用依賴倒置原則可以減少類間的耦合性仆葡,提高系統(tǒng)的穩(wěn)定性,減少并行開發(fā)引起的風(fēng)險志笼,提高代碼的可讀性和可維護性沿盅。

什么是高層模塊,什么是低層模塊纫溃?
在項目中我們經(jīng)常會有一些數(shù)學(xué)函數(shù)庫腰涧,或者工具類(Log日志工具),這些封裝好的工具會被我們業(yè)務(wù)邏輯模塊經(jīng)常調(diào)用紊浩,那么這些工具函數(shù)庫就是高層模塊窖铡,業(yè)務(wù)邏輯模塊就是低層模塊疗锐。

什么是細節(jié),什么是抽象万伤?
細節(jié)的意思就是具體的實現(xiàn)窒悔,例如上面里氏替換中打電話的例子,在不使用里氏替換的時候需要定義4種打電話函數(shù)敌买,這就是依賴細節(jié)简珠,其中的細節(jié)就是“OnePlus ”、“Pixel ”虹钮、“XiaoMi”聋庵、“Apple ”,反之抽象就是Phone 芙粱。

簡例:華碩和微型都可使用不同型號的顯卡祭玉,反之不同型號的顯卡也可以使用在不同品牌的主板上。利用依賴倒置原則春畔,這種規(guī)則的實現(xiàn)變得很簡單也很靈活~

示例代碼

//顯卡
public interface IGraphicsCard
{
    void BeginWork(IMainboard mainboard);
}
//主板
public interface IMainboard
{
    void GetElectricity();
    void DrawPicture(IGraphicsCard graphicsCard);
}


public class NVIDIA_2018Ti : IGraphicsCard
{
    public NVIDIA_2018Ti(IMainboard mainboard)
    {
        BeginWork(mainboard);
    }
    public void BeginWork(IMainboard mainboard)
    {
        mainboard.GetElectricity();
        Debug.Log($"NVIDIA_2018Ti獲取{mainboard.GetType()}電量后開始工作");
    }
}
public class NVIDIA_2018 : IGraphicsCard
{
    public NVIDIA_2018(IMainboard mainboard)
    {
        BeginWork(mainboard);
    }
    public void BeginWork(IMainboard mainboard)
    {
        mainboard.GetElectricity();
        Debug.Log($"NVIDIA_2018獲取{mainboard.GetType()}電量后開始工作");
    }
}

//華碩主板
public class Asus : IMainboard
{
    public void DrawPicture(IGraphicsCard graphicsCard) { }
    public void GetElectricity() { }
}
//微型主板
public class MSI : IMainboard
{
    public void DrawPicture(IGraphicsCard graphicsCard) { }
    public void GetElectricity() { }
}

實現(xiàn)代碼脱货,這種2*2種模式的實現(xiàn)非常簡單~

    public void DrawPicture()
    {
        IMainboard aSus = new Asus();
        aSus.DrawPicture(new NVIDIA_2018Ti(aSus));
        aSus.DrawPicture(new NVIDIA_2018(aSus));

        IMainboard mSI = new MSI();
        mSI.DrawPicture(new NVIDIA_2018Ti(mSI));
        mSI.DrawPicture(new NVIDIA_2018(mSI));
    }

[4]

4. 接口隔離原則(Interface Segregation Principle):
提供盡可能小的單獨接口,而不要提供大的總接口律姨。具體行為讓實現(xiàn)類了解越少越好振峻。
盡量細化接口,接口中的方法盡量少择份。也就是要為各個類建立專用的接口扣孟,而不要試圖去建立一個很龐大的接口供所有依賴它的類去調(diào)用。依賴幾個專用的接口要比依賴一個綜合的接口更靈活荣赶。接口是設(shè)計時對外部設(shè)定的約定凤价,通過分散定義多個接口,可以預(yù)防外來變更的擴散拔创,提高系統(tǒng)的靈活性和可維護性利诺。
通俗的講就是定義的接口盡量按照功能細分,比如打電話功能一個接口伏蚊,上網(wǎng)一個接口立轧,發(fā)短信一個接口。接口粒度小不僅職能明確躏吊,也不會因為使用某種職能而必須實現(xiàn)一些不必要的功能氛改。

示例代碼



[5]

5. 迪米特法則(Law Of Demeter)又稱【最少知識原則】:
類與類之間的關(guān)系越密切,耦合度越大比伏,只有降低類與類之間的耦合才符合設(shè)計模式胜卤;對于被依賴的類來說,無論邏輯多復(fù)雜都要盡量封裝在類的內(nèi)部赁项。
每個對象都會與其他對象有耦合關(guān)系葛躏,我們稱出現(xiàn)成員變量澈段、方法參數(shù)、方法返回值中的類為直接的耦合依賴舰攒,而出現(xiàn)在局部變量中的類則不是直接耦合依賴败富,也就是說,不是直接耦合依賴的類最好不要作為局部變量的形式出現(xiàn)在類的內(nèi)部摩窃。
一個對象對另一個對象知道的越少越好兽叮,即一個實體應(yīng)當盡可能少的與其他實體發(fā)生相互作用,在一個類里降低引用其他類猾愿,尤其是局部變量的依賴類鹦聪,能省則省。同時兩個類不必彼此直接通信蒂秘,那么這兩個類就不必發(fā)生直接的相互作用泽本。如果其中一個類需要調(diào)用另一個類的某一方法的話,可以通過第三者轉(zhuǎn)發(fā)這個調(diào)用姻僧。

表達的意思是能用 private规丽、protected的就不要用public,不要過多的暴露自己的內(nèi)容撇贺,而且對應(yīng)類與類之間的關(guān)系嘁捷,盡量越少越好。后面講到的門面模式中介者模式想表達的也是這個意思显熏。

迪米特法則其根本思想,是強調(diào)了類之間的松耦合晒屎。類之間的耦合越弱喘蟆,一個處于弱耦合的類被修改,對有關(guān)類造成波及的影響越小鼓鲁。

示例代碼
注:這種情況就違背了迪米特法則蕴轨。因為其他的類不需要這個Log擴展,這也就破壞了原來的結(jié)構(gòu)骇吭,侵入性太強了橙弱。如果所有的擴展都是以O(shè)bject為基準,那么調(diào)用函數(shù)的時候就會造成下拉函數(shù)條目過多燥狰。

public static class Exted
{
    public static void CustomerLog_Obj0(this GameObject Obj) { }

    public static void CustomerLog_Obj1(this object Obj) { }
    public static void CustomerLog_Obj2(this object Obj) { }

    public static void CustomerLog_Obj3(this object Obj) { }
    public static void CustomerLog_Obj4(this object Obj) { }
}

[6]

6. 開閉原則(Open Close Principle):
主要體現(xiàn)對擴展開放棘脐、對修改封閉,意味著有新的需求或變化時龙致,可以對現(xiàn)有代碼進行擴展蛀缝,以適應(yīng)新的情況。軟件需求總是變化的目代,世界上沒有一個軟件是不變的屈梁,因此對軟件設(shè)計人員來說嗤练,在不需要對原有系統(tǒng)進行修改的情況下,實現(xiàn)靈活的系統(tǒng)擴展在讶。
例如通過模板方法模式策略模式進行重構(gòu)煞抬,實現(xiàn)對修改封閉,對擴展開放的設(shè)計思路构哺。
封裝變化革答,是實現(xiàn)開閉原則的重要手段,對于經(jīng)常發(fā)生變化的狀態(tài)遮婶,將其封裝為一個抽象蝗碎,但拒絕濫用抽象,只將經(jīng)常變化的部分進行抽象旗扑。
通俗的講在功能變動的時候蹦骑,盡量以增量補丁的形式更改,也就是原來代碼保持不變的同時進行更改臀防。


[7]

7. 組合/聚合復(fù)用原則(Composite/Aggregate Reuse Principle CARP):
整個設(shè)計模式就是在講如何合理安排類與類之間的組合/聚合眠菇。在一個新的對象里面通過關(guān)聯(lián)關(guān)系,使一些已有的對象成為新對象的一部分袱衷,新對象通過委派調(diào)用已有對象的方法捎废,達到復(fù)用其已有功能的目的。也就是致燥,要盡量使用類的合成復(fù)用登疗,不要使用繼承。
繼承復(fù)用破壞數(shù)據(jù)封裝性嫌蚤,將基類的實現(xiàn)細節(jié)全部暴露給了派生類辐益,基類的內(nèi)部細節(jié)常常對派生類是透明的。白箱復(fù)用脱吱,雖然簡單智政,但不安全,不能在程序的運行過程中隨便改變箱蝠⌒妫基類的實現(xiàn)發(fā)生了改變,派生類的實現(xiàn)也不得不改變宦搬;從基類繼承而來的派生類是固定的牙瓢,不能在運行時發(fā)生改變,因此沒有足夠的靈活性床三。
組合/聚合復(fù)用原則可以使系統(tǒng)更加靈活一罩,類與類之間的耦合度降低,一個類的變化對其他類造成的影響相對較少撇簿,因此一般首選使用組合/聚合來實現(xiàn)復(fù)用聂渊;其次才考慮繼承差购,在使用繼承時,需要嚴格遵循里氏代換原則汉嗽,有效使用繼承會有助于對問題的理解欲逃,降低復(fù)雜度,而濫用繼承反而會增加系統(tǒng)構(gòu)建和維護的難度以及系統(tǒng)的復(fù)雜度饼暑,因此需要慎重使用繼承復(fù)用稳析。
核心思想:組合優(yōu)于繼承


  1. 單一職責(zé)原則腳注結(jié)尾 ?

  2. 里氏替換原則腳注結(jié)尾 ?

  3. 依賴倒置原則腳注結(jié)尾 ?

  4. 接口隔離原則腳注結(jié)尾 ?

  5. 迪米特法則腳注結(jié)尾 ?

  6. 開閉原則腳注結(jié)尾 ?

  7. 組合/聚合復(fù)用原則腳注結(jié)尾 ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市弓叛,隨后出現(xiàn)的幾起案子彰居,更是在濱河造成了極大的恐慌,老刑警劉巖撰筷,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件陈惰,死亡現(xiàn)場離奇詭異,居然都是意外死亡毕籽,警方通過查閱死者的電腦和手機抬闯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來关筒,“玉大人溶握,你說我怎么就攤上這事≌舨ィ” “怎么了睡榆?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長袍榆。 經(jīng)常有香客問我肉微,道長,這世上最難降的妖魔是什么蜡塌? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮勿负,結(jié)果婚禮上馏艾,老公的妹妹穿的比我還像新娘。我一直安慰自己奴愉,他們只是感情好琅摩,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著锭硼,像睡著了一般房资。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上檀头,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天轰异,我揣著相機與錄音岖沛,去河邊找鬼。 笑死搭独,一個胖子當著我的面吹牛婴削,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播牙肝,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼唉俗,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了配椭?” 一聲冷哼從身側(cè)響起虫溜,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎股缸,沒想到半個月后衡楞,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡乓序,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年寺酪,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片替劈。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡寄雀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出陨献,到底是詐尸還是另有隱情盒犹,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布眨业,位于F島的核電站急膀,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏龄捡。R本人自食惡果不足惜卓嫂,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望聘殖。 院中可真熱鬧晨雳,春花似錦、人聲如沸奸腺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽突照。三九已至帮非,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背末盔。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工筑舅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人庄岖。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓豁翎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親隅忿。 傳聞我的和親對象是個殘疾皇子心剥,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 面向?qū)ο蟮?個基本要素: 封裝、繼承背桐、多態(tài) 面向?qū)ο蟮?個基本設(shè)計原則: 單一職責(zé)原則(Single-Respos...
    badcyc閱讀 855評論 0 4
  • 本文出自《Android源碼設(shè)計模式解析與實戰(zhàn)》中的第一章优烧。 1、優(yōu)化代碼的第一步——單一職責(zé)原則 單一職責(zé)原則的...
    MrSimp1e0閱讀 1,766評論 1 13
  • 單一職責(zé)原則 (SRP) 全稱 SRP , Single Responsibility Principle 單一職...
    米莉_L閱讀 1,764評論 2 5
  • 手機和電腦 兒子對手機和電腦有了一定的自覺性和自控力链峭。 今天是臘月二十四畦娄,是北方的小年,兒子的晚自習(xí)時間和明天即周...
    雪霽晴空喜迎春閱讀 201評論 0 2
  • 微習(xí)慣是什么呢弊仪?它并非是像我這樣明明平時七點起床熙卡,而今天早晨為了昨天立下的flag五點半就起來了,它其實是每天都比...
    麻麻不完美閱讀 393評論 0 0