超易懂埋同!原來SOLID原則要這么理解州叠!

image.png

說到 SOLID 原則,相信有過幾年工作經(jīng)驗的朋友都有個大概印象莺禁,但就是不知道它具體是什么留量。甚至有些工作了十幾年的朋友,它們對 SOLID 原則的理解也停留在表面哟冬。今天我們就來聊聊 SOLID 原則以及它們之間的關系。

什么是SOLID原則

SOLID 原則其實是用來指導軟件設計的忆绰,它一共分為五條設計原則浩峡,分別是:

  • 單一職責原則(SRP)
  • 開閉原則(OCP)
  • 里氏替換原則(LSP)
  • 接口隔離原則(ISP)
  • 依賴倒置原則(DIP)

單一職責原則(SRP)

單一職責原則(Single Responsibility Principle),它的定義是:應該有且僅有一個原因引起類的變更错敢。簡單地說:接口職責應該單一翰灾,不要承擔過多的職責。 用生活中肯德基的例子來舉例:負責前臺收銀的服務員稚茅,就不要去餐廳收盤子纸淮。負責餐廳收盤子的就不要去做漢堡。

單一職責適用于接口亚享、類咽块,同時也適用于方法。例如我們需要修改用戶密碼欺税,有兩種方式可以實現(xiàn)侈沪,一種是用「修改用戶信息接口」實現(xiàn)修改密碼,一種是新起一個接口來實現(xiàn)修改密碼功能晚凿。在單一職責原則的指導下亭罪,一個方法只承擔一個職能,所以我們應該新起一個接口來實現(xiàn)修改密碼的功能歼秽。

單一職責原則的重點在于職責的劃分应役,很多時候并不是一成不變的,需要根據(jù)實際情況而定。單一職責能夠使得類復雜性降低箩祥、類之間職責清晰院崇、代碼可讀性提高、更加容易維護滥比。但它的缺點也很明顯亚脆,就是對技術(shù)人員要求高,有些時候職責難以區(qū)分盲泛。

我們在設計一個類的時候濒持,可以先從粗粒度的類開始設計,等到業(yè)務發(fā)展到一定規(guī)模寺滚,我們發(fā)現(xiàn)這個粗粒度的類方法和屬性太多柑营,且經(jīng)常修改的時候,我們就可以對這個類進行重構(gòu)了村视,將這個類拆分成粒度更細的類官套,這就是所謂的持續(xù)重構(gòu)。

開閉原則(OCP)

開閉原則(Open Closed Principle)蚁孔,它的定義是:一個軟件實體奶赔,如類、模塊和函數(shù)應該對擴展開放杠氢,對修改關閉站刑。簡單地說:就是當別人要修改軟件功能的時候,使得他不能修改我們原有代碼鼻百,只能新增代碼實現(xiàn)軟件功能修改的目的绞旅。

這聽著有點玄乎,我來舉個例子吧温艇。

這段代碼模擬的是對于水果剝皮的處理程序因悲。如果是蘋果,那么是一種撥皮方法勺爱;如果是香蕉晃琳,則是另一種剝皮方法。如果以后還需要處理其他水果邻寿,那么就會在后面加上很多 if else 語句蝎土,最終會讓整個方法變得又臭又長。如果恰好這個水果中的不同品種有不同的剝皮方法绣否,那么這里面又會有很多層嵌套誊涯。

if(type == apple){
    //deal with apple 
} else if (type == banana){
    //deal with banana
} else if (type == ......){
    //......
}

可以看得出來,上面這樣的代碼并沒有滿足「對拓展開放蒜撮,對修改封閉」的原則暴构。每次需要新增一種水果跪呈,都可以直接在原來的代碼上進行修改。久而久之取逾,整個代碼塊就會變得又臭又長耗绿。

如果我們對剝水果皮這件事情做一個抽象,剝蘋果皮是一個具體的實現(xiàn)砾隅,剝香蕉皮是一個具體的實現(xiàn)误阻,那么寫出的代碼會是這樣的:

public interface PeelOff {
    void peelOff();
}

public class ApplePeelOff implement PeelOff{
    void peelOff(){
        //deal with apple
    }
}

public class BananaPeelOff implement PeelOff{
    void peelOff(){
        //deal with banan
    }
}

public class PeelOffFactory{
    private Map<String, PeelOff> map = new HashMap();

    private init(){
        //init all the Class that implements PeelOff interface 
      }
}

.....

public static void main(){
    String type = "apple";
    PeelOff peelOff = PeelOffFactory.getPeelOff(type);  //get ApplePeelOff Class Instance.
    peelOff.pealOff();
}

上面這種實現(xiàn)方式使得別人無法修改我們的代碼,為什么晴埂?

因為當需要對西瓜剝皮的時候究反,他會發(fā)現(xiàn)他只能新增一個類實現(xiàn) PeelOff 接口,而無法再原來的代碼上修改儒洛。這樣就實現(xiàn)了「對拓展開放精耐,對修改封閉」的原則。

里氏替換原則(LSP)

里氏替換原則(LSP)的定義是:所有引用基類的地方必須能透明地使用其子類的對象琅锻。簡單地說:所有父類能出現(xiàn)的地方卦停,子類就可以出現(xiàn),并且替換了也不會出現(xiàn)任何錯誤恼蓬。 例如下面 Parent 類出現(xiàn)的地方惊完,可以替換成 Son 類,其中 Son 是 Parent 的子類处硬。

Parent obj = new Son();
等價于
Son son  = new Son();

這樣的例子在 Java 語言中是非常常見的专执,但其核心要點是:替換了也不會出現(xiàn)任何的錯誤。這就要求子類的所有相同方法郁油,都必須遵循父類的約定,否則當父類替換為子類時就會出錯攀痊。 這樣說可能還是有點抽象桐腌,我舉個例子。

public class Parent{
    // 定義只能扔出空指針異常
    public void hello throw NullPointerException(){
    }
}
public class Son extends Parent{
    public void hello throw NullPointerException(){
        // 子類實現(xiàn)時卻扔出所有異常
        throw Exception;
    }
}

上面的代碼中苟径,父類對于 hello 方法的定義是只能扔出空指針異常案站,但子類覆蓋父類的方法時,卻扔出了其他異常棘街,違背了父類的約定蟆盐。那么當父類出現(xiàn)的地方,換成了子類遭殉,那么必然會出錯石挂。

其實這個例子舉得不是很好,因為這個在編譯層面可能就有錯誤险污。但表達的意思應該是到位了痹愚。

而這里的父類的約定富岳,不僅僅指的是語法層面上的約定,還包括實現(xiàn)上的約定拯腮。有時候父類會在類注釋窖式、方法注釋里做了相關約定的說明,當你要覆寫父類的方法時动壤,需要弄懂這些約定萝喘,否則可能會出現(xiàn)問題。例如子類違背父類聲明要實現(xiàn)的功能琼懊。比如父類某個排序方法是從小到大來排序阁簸,你子類的方法竟然寫成了從大到小來排序。

里氏替換原則 LSP 重點強調(diào):對使用者來說肩碟,能夠使用父類的地方强窖,一定可以使用其子類,并且預期結(jié)果是一致的削祈。

接口隔離原則(ISP)

接口隔離原則(Interface Segregation Principle)的定義是:類間的依賴關系應該建立在最小的接口上翅溺。簡單地說:接口的內(nèi)容一定要盡可能地小,能有多小就多小髓抑。

舉個例子來說咙崎,我們經(jīng)常會給別人提供服務,而服務調(diào)用方可能有很多個吨拍。很多時候我們會提供一個統(tǒng)一的接口給不同的調(diào)用方褪猛,但有些時候調(diào)用方 A 只使用 1、2羹饰、3 這三個方法伊滋,其他方法根本不用。調(diào)用方 B 只使用 4队秩、5 兩個方法笑旺,其他都不用。接口隔離原則的意思是馍资,你應該把 1筒主、2、3 抽離出來作為一個接口鸟蟹,4乌妙、5 抽離出來作為一個接口,這樣接口之間就隔離開來了建钥。

那么為什么要這么做呢藤韵?我想這是為了隔離變化吧! 想想看锦针,如果我們把 1荠察、2置蜀、3、4悉盆、5 放在一起盯荤,那么當我們修改了 A 調(diào)用方才用到 的 1 方法,此時雖然 B 調(diào)用方根本沒用到 1 方法焕盟,但是調(diào)用方 B 也會有發(fā)生問題的風險秋秤。而如果我們把 1、2脚翘、3 和 4灼卢、5 隔離成兩個接口了,我修改 1 方法来农,絕對不會影響到 4鞋真、5 方法。

除了改動導致的變化風險之外沃于,其實還會有其他問題涩咖,例如:調(diào)用方 A 抱怨,為什么我只用 1繁莹、2檩互、3 方法,你還要寫上 4咨演、5 方法闸昨,增加我的理解成本。調(diào)用方 B 同樣會有這樣的困惑薄风。

在軟件設計中饵较,ISP 提倡不要將一個大而全的接口扔給使用者,而是將每個使用者關注的接口進行隔離遭赂。

依賴倒置原則(DIP)

依賴倒置原則(Dependence Inversion Principle)的定義是:高層模塊不應該依賴底層模塊告抄,兩者都應該依賴其抽象。抽象不應該依賴細節(jié)嵌牺,即接口或抽象類不依賴于實現(xiàn)類。細節(jié)應該依賴抽象龄糊,即實現(xiàn)類不應該依賴于接口或抽象類逆粹。簡單地說,就是說我們應該面向接口編程炫惩。通過抽象成接口僻弹,使各個類的實現(xiàn)彼此獨立,實現(xiàn)類之間的松耦合他嚷。

如果我們每個人都能通過接口編程蹋绽,那么我們只需要約定好接口定義芭毙,我們就可以很好地合作了。軟件設計的 DIP 提倡使用者依賴一個抽象的服務接口卸耘,而不是去依賴一個具體的服務執(zhí)行者退敦,從依賴具體實現(xiàn)轉(zhuǎn)向到依賴抽象接口,倒置過來蚣抗。

SOLID 原則的本質(zhì)

我們總算把 SOLID 原則中的五個原則說完了侈百。但說了這么一通,好像是懂了翰铡,但是好像什么都沒記住钝域。 那么我們就來盤一盤他們之間的關系。ThoughtWorks 上有一篇文章說得挺不錯锭魔,它說:

  • 單一職責是所有設計原則的基礎例证,開閉原則是設計的終極目標。
  • 里氏替換原則強調(diào)的是子類替換父類后程序運行時的正確性迷捧,它用來幫助實現(xiàn)開閉原則织咧。
  • 而接口隔離原則用來幫助實現(xiàn)里氏替換原則,同時它也體現(xiàn)了單一職責党涕。
  • 依賴倒置原則是過程式編程與面向?qū)ο缶幊痰姆炙畮X烦感,同時它也被用來指導接口隔離原則。
圖片l來自ThoughtWorks

簡單地說:依賴倒置原則告訴我們要面向接口編程膛堤。當我們面向接口編程之后手趣,接口隔離原則和單一職責原則又告訴我們要注意職責的劃分,不要什么東西都塞在一起肥荔。當我們職責捋得差不多的時候绿渣,里氏替換原則告訴我們在使用繼承的時候,要注意遵守父類的約定燕耿。而上面說的這四個原則中符,它們的最終目標都是為了實現(xiàn)開閉原則。

參考資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蚜锨,一起剝皮案震驚了整個濱河市档插,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌亚再,老刑警劉巖郭膛,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異氛悬,居然都是意外死亡则剃,警方通過查閱死者的電腦和手機耘柱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棍现,“玉大人调煎,你說我怎么就攤上這事≈嵩郏” “怎么了汛蝙?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵,是天一觀的道長朴肺。 經(jīng)常有香客問我窖剑,道長,這世上最難降的妖魔是什么戈稿? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任西土,我火速辦了婚禮,結(jié)果婚禮上鞍盗,老公的妹妹穿的比我還像新娘需了。我一直安慰自己,他們只是感情好般甲,可當我...
    茶點故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布肋乍。 她就那樣靜靜地躺著,像睡著了一般敷存。 火紅的嫁衣襯著肌膚如雪墓造。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天锚烦,我揣著相機與錄音觅闽,去河邊找鬼。 笑死涮俄,一個胖子當著我的面吹牛蛉拙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播彻亲,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼孕锄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了苞尝?” 一聲冷哼從身側(cè)響起硫惕,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎野来,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體踪旷,經(jīng)...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡曼氛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年豁辉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舀患。...
    茶點故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡徽级,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出聊浅,到底是詐尸還是另有隱情餐抢,我是刑警寧澤,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布低匙,位于F島的核電站旷痕,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏顽冶。R本人自食惡果不足惜欺抗,卻給世界環(huán)境...
    茶點故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望强重。 院中可真熱鬧绞呈,春花似錦、人聲如沸间景。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽倘要。三九已至圾亏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間碗誉,已是汗流浹背召嘶。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留哮缺,地道東北人弄跌。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像尝苇,于是被迫代替她去往敵國和親铛只。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,922評論 2 361

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

  • 盡管大家都認為SOLID是非常重要的設計原則蜕着,并且對每一條原則都耳熟能詳,但我發(fā)現(xiàn)大部分開發(fā)者并沒有真正理解。要獲...
    Meixuesong閱讀 448評論 0 2
  • 超前的設計或者過度的設計都不是良好的設計承匣,很多時候我們等到代碼在第一次變化的時候可以及時作出反應就夠了 單一責任原...
    Corbin___閱讀 823評論 0 0
  • 關于軟件的設計原則其實很多蓖乘,其中比較經(jīng)典的有SOLID、KISS韧骗、YAGNI嘉抒、DRY、LOD 等袍暴。SOLID原則并...
    貓清揚閱讀 1,377評論 0 7
  • 認識 SOLID 原則 無論是軟件系統(tǒng)設計政模,還是代碼實現(xiàn)岗宣,遵循有效和明確的設計原則,都利于系統(tǒng)軟件靈活可靠览徒,安全快...
    聞人的技術(shù)博客閱讀 841評論 0 7
  • 久違的晴天狈定,家長會。 家長大會開好到教室時习蓬,離放學已經(jīng)沒多少時間了纽什。班主任說已經(jīng)安排了三個家長分享經(jīng)驗。 放學鈴聲...
    飄雪兒5閱讀 7,528評論 16 22