設(shè)計(jì)模式之設(shè)計(jì)原則

image.png

“開一閉” 原則(OCP)

經(jīng)典力學(xué)的基石是牛頓三大定律。 而面向?qū)ο蟮目蓮?fù)用設(shè)計(jì) (Object Oriented Design, 或 OOD) 的第一塊基石,便是所謂的”開-閉“原則 (Open-Closed Principle, 酬侵瑁縮寫為OCP)猬仁。

“開-閉 ” 原則講的是:一個(gè)軟件實(shí)體應(yīng)當(dāng)對(duì)擴(kuò)展開放师痕, 對(duì)修改關(guān)閉。 這一原則最早由 Bertrand Meyer [MEYER88]提出丑婿, 英文原文是:Software entities should be open for extension, but closed for modification.

這個(gè)原則說的是, 在設(shè)計(jì)一個(gè)模塊的時(shí)候, 應(yīng)當(dāng)使這個(gè)模塊可以在不被修改的前提下被擴(kuò)展羹奉。 換言之秒旋, 應(yīng)當(dāng)可以在不必修改源代碼的情況下改變這個(gè)模塊的行為。

所有的軟件系統(tǒng)都有一個(gè)共同的性質(zhì)诀拭, 即對(duì)它們的需求都會(huì)隨時(shí)間的推移而發(fā)生變化迁筛。 在軟件系統(tǒng)面臨新的需求時(shí), 系統(tǒng)的設(shè)計(jì)必須是穩(wěn)定的耕挨。 滿足 “開-閉” 原則的設(shè)計(jì)可以給一個(gè)軟件系統(tǒng)兩個(gè)無可比擬的優(yōu)越性:

  • 通過擴(kuò)展已有的軟件系統(tǒng)细卧, 可以提供新的行為, 以滿足對(duì)軟件的新需求筒占, 使變化中的軟件系統(tǒng)有一定的適應(yīng)性和靈活性贪庙。
  • 已有的軟件模塊,特別是最重要的抽象層模塊不能再修改翰苫, 這就使變化中的軟件系統(tǒng)有一定的穩(wěn)定性和延續(xù)性止邮。

具有這兩個(gè)優(yōu)點(diǎn)的軟件系統(tǒng)是一個(gè)在高層次上實(shí)現(xiàn)了復(fù)用的系統(tǒng), 也是一個(gè)易于維護(hù) 的系統(tǒng)奏窑。

里氏代換原則(LSP)

從“開-閉 ” 原則中可以看出面向?qū)ο笤O(shè)計(jì)的重要原則是創(chuàng)建抽象化导披,并且從抽象化導(dǎo)出具體化。 具體化可以給出不同的版本良哲, 每個(gè)版本都給出不同的實(shí)現(xiàn)盛卡。
從抽象化到具體化的導(dǎo)出要使用繼承關(guān)系和這里要引入的里氏代換原則 (Liskov Substitution Principle, 常縮寫為 LSP) 筑凫。 里氏代換原則由 Barbara Liskov 提出滑沧。

里氏代換原則的嚴(yán)格表達(dá)是:
如果對(duì)每一個(gè)類型為T1的對(duì)象o1,都有類型為T2的對(duì)象o2,使得以Tl定義的所有程序P在所有的對(duì)象o1都代換成o2時(shí),程序P的行為沒有變化巍实,那么類型T2是類型T1的子類型滓技。

換言之,一個(gè)軟件實(shí)體如果使用的是一個(gè)基類的話棚潦,那么一定適用于其子類令漂,而且它根本不能察覺出基類對(duì)象和子類對(duì)象的區(qū)別。

比如丸边,假設(shè)有兩個(gè)類叠必,一個(gè)是Base類,另一個(gè)是Derived類妹窖,而且Derived類是Base類的子類纬朝,那么一個(gè)方法如果可以接受一個(gè)基類對(duì)象b的話:

method1(Base b)

那么它必然可以接受一個(gè)子類對(duì)象d,也即可以有methodl(d)。
里氏代換原則是繼承復(fù)用的基石骄呼。只有當(dāng)衍生類可以替換掉基類共苛,軟件單位的功能不會(huì)受到影響時(shí)判没,基類才能真正被復(fù)用,而衍生類也才能夠在基類的基礎(chǔ)上增加新的行為隅茎。

反過來的代換不成立澄峰。

依賴倒轉(zhuǎn)原則(DIP)

實(shí)現(xiàn) “ 開-閉 ” 原則的關(guān)鍵是抽象化, 并且從抽象化導(dǎo)出具體化實(shí)現(xiàn)辟犀。 如果說 “ 開-閉 ” 原則是面向?qū)ο笤O(shè)計(jì)的目標(biāo)的話俏竞, 依賴倒轉(zhuǎn)原則就是這個(gè)面向?qū)ο笤O(shè)計(jì)的主要機(jī)制[MARTIN00] 。

依賴倒轉(zhuǎn)原則講的是: 要依賴于抽象踪蹬, 不要依賴于具體.

簡(jiǎn)單地說胞此, 依賴倒轉(zhuǎn)原則 (Dependence Inversion Principle) 要求客戶端依賴于抽象耦合。 依賴倒轉(zhuǎn)原則的表述是:

抽象不應(yīng)當(dāng)依賴于細(xì)節(jié)跃捣,細(xì)節(jié)應(yīng)當(dāng)依賴于抽象漱牵。
(Abstractions should not depend upon details. Details should depend upon abstractions)

依賴倒轉(zhuǎn)原則的另一種表述是:

要針對(duì)接口編程, 不要針對(duì)實(shí)現(xiàn)編程疚漆。
(Program to an interface, not an implement-ation)

第二種表述是[GOF95]一書所強(qiáng)調(diào)的酣胀。
針對(duì)接口編程的意思就是說, 應(yīng)當(dāng)使用 Java 接口和抽象 Java 類進(jìn)行變量的類型聲明娶聘、參量的類型聲明闻镶、 方法的返還類型聲明, 以及數(shù)據(jù)類型的轉(zhuǎn)換等丸升。

不要針對(duì)實(shí)現(xiàn)編程的意思就是說铆农, 不應(yīng)當(dāng)使用具體 Java 類進(jìn)行變量的類型聲明、 參 量的類型聲明狡耻、 方法的返還類型聲明墩剖, 以及數(shù)據(jù)類型的轉(zhuǎn)換等。

要保證這一點(diǎn)夷狰,一個(gè)具體Java類應(yīng)當(dāng)只實(shí)現(xiàn)Java接口和抽象Java類中聲明過的方法岭皂,而不應(yīng)當(dāng)給出多余的方法。

倒轉(zhuǎn)依賴關(guān)系強(qiáng)調(diào)一個(gè)系統(tǒng)內(nèi)的實(shí)體之間關(guān)系的靈活性沼头∫妫基本上,如果設(shè)計(jì)師希望遵循”開-閉“原則进倍,那么倒轉(zhuǎn)依賴原則便是達(dá)到要求的途徑土至。

接口隔離原則(ISP)

接口隔離原則 (Interface Segregation Principle, 常常略寫做 ISP) 講的是: 使用多個(gè)專門的接口比使用單一的總接口要好。
換言之猾昆, 從一個(gè)客戶類的角度來講: 一個(gè)類對(duì)另外一個(gè)類的依賴性應(yīng)當(dāng)是建立在最小的接口上的毙籽。

人們所說的 “接口” 往往是指兩種不同的東西: 一種是指 Java 語言中的有嚴(yán)格定義的 Interface 結(jié)構(gòu), 比如java.lang.Runnable 就是一個(gè) Java 接口毡庆;另一種就是一個(gè)類型所具有的方法特征的集合坑赡,也稱做 “接口”,但僅是一種邏輯上的抽象么抗。

應(yīng)于這兩種不同的用詞毅否, 接口隔離原則的表達(dá)方式以及含義都有所不同。

過于臃腫的接口是對(duì)接口的污染(InterfaceContamination)蝇刀。

與迪米特法則的關(guān)系
迪米特法則要求任何一個(gè)軟件實(shí)體螟加,除非絕對(duì)需要,不然不要與外界通信吞琐。即使必須進(jìn)行通信捆探,也應(yīng)當(dāng)盡量限制通信的廣度和深度。

顯然站粟,定制服務(wù)原則拒絕向客戶端提供不需要提供的行為黍图,是符合迪米特法則的。

合成/聚合復(fù)用原則 (CARP)

合成/聚合復(fù)用原則 (Composite/Aggregate Reuse Principle, 或 CARP) 經(jīng)常又叫做合成復(fù)用原則 (Composite Reuse Principle 或 CRP) 奴烙。 合成/聚合復(fù)用原則就是在一個(gè)新的對(duì)象里面使用一些已有的對(duì)象助被, 使之成為新對(duì)象的部分;新的對(duì)象通過向這些對(duì)象的委派達(dá)到復(fù)用已有功能的目的切诀。

這個(gè)設(shè)計(jì)原則有另一個(gè)更簡(jiǎn)短的表述: 要盡量使用合成/聚合揩环, 盡量不要使用繼承。

合成 (Composite) 一詞的使用很廣泛幅虑, 經(jīng)常導(dǎo)致混淆丰滑。 為避免這些混淆, 不妨先來考察一下 “ 合成” 與 “聚合” 的區(qū)別倒庵。

合成和聚合的區(qū)別

合成 (Composition) 和聚合 (Aggregation) 均是關(guān)聯(lián) (Association) 的特殊種類褒墨。 聚合用來表示 “擁有” 關(guān)系或者整體與部分的關(guān)系;而合成則用來表示一種強(qiáng)得多的 “擁有” 關(guān)系哄芜。 在一個(gè)合成關(guān)系里貌亭, 部分和整體的生命周期是一樣的。 一個(gè)合成的新的對(duì)象完全擁有對(duì)其組成部分的支配權(quán)认臊, 包括它們的創(chuàng)建和湮滅等圃庭。 使用程序語言的術(shù)語來講, 組合而成的新對(duì)象對(duì)組成部分的內(nèi)存分配失晴、 內(nèi)存釋放有絕對(duì)的責(zé)任剧腻。

更進(jìn)一步來講, 一個(gè)合成的多重性 (Multiplicity) 不能超過1, 換言之涂屁, —個(gè)合成關(guān)系中的成分對(duì)象是不能與另一個(gè)合成關(guān)系共享的书在。 一個(gè)成分對(duì)象在同一個(gè)時(shí)間內(nèi)只能屬于一個(gè)合成關(guān)系。 如果一個(gè)合成關(guān)系湮滅了拆又, 那么所有的成分對(duì)象要么自己湮滅所有的成分對(duì)象(這種情況較為普遍)儒旬, 要么就得將這責(zé)任交給別人(這種情況較為罕見)栏账。

用 C 程序員較易理解的語言來講, 合成是值的聚合 (Aggregation by Value), 而通常所說的聚合則是引用的聚合 (Aggregation by Reference) 栈源。

迪米特法則(LoD)

迪米特法則 (Law of Demeter 或簡(jiǎn)寫為 LoD) 又叫做最少知識(shí)原則 (Least Knowledge Principle 或簡(jiǎn)寫為 LKP), 就是說挡爵,一個(gè)對(duì)象應(yīng)當(dāng)對(duì)其他對(duì)象有盡可能少的了解。

迪米特法則最初是用來作為面向?qū)ο蟮南到y(tǒng)設(shè)計(jì)風(fēng)格的一種法則甚垦, 于 1987 年秋天由Ian Holland 在美國東北大學(xué) (Northeastern University) 為一個(gè)叫做迪米特 (Demeter) 的項(xiàng)目設(shè)計(jì)提出的茶鹃, 因此叫做迪米特法則[LIEB89] [LIEB86] 。 這條法則實(shí)際上是很多著名系統(tǒng)艰亮, 比如火星登錄軟件系統(tǒng)闭翩、 木星的歐羅巴衛(wèi)星軌道飛船的軟件系統(tǒng)的指導(dǎo)設(shè)計(jì)原則。

迪米特法則的各種表述

沒有任何一個(gè)其他的OO設(shè)計(jì)原則像迪米特法則這樣有如此之多的表述方式迄埃, 下面給出的也只是眾多的表述中較有代表性的幾種:

  • 只與你直接的朋友們通信 (Only talk to your immediate friends)疗韵。
  • 不要跟 “ 陌生人” 說話 (Don't talk to strangers)。
  • 每一個(gè)軟件單位對(duì)其他的單位都只有最少的知識(shí)调俘, 而且局限于那些與本單位密切相關(guān)的軟件單位伶棒。

在上面的表述里面, 什么是 “ 直接 ”彩库、”陌生” 和 “ 密切 ” 則被有意地模糊化了肤无, 以便在不同的環(huán)境下可以有不同的解釋。

狹義的迪米特法則

如果兩個(gè)類不必彼此直接通信骇钦,那么這兩個(gè)類就不應(yīng)當(dāng)發(fā)生直接的相互作用宛渐。 如果其中的一個(gè)類需要調(diào)用另一個(gè)類的某一個(gè)方法的話,可以通過第三者轉(zhuǎn)發(fā)這個(gè)調(diào)用眯搭。

狹義的迪米特法則的缺點(diǎn)

遵循狹義的迪米特法則會(huì)產(chǎn)生一個(gè)明顯的缺點(diǎn):會(huì)在系統(tǒng)里造出大量的小方法窥翩,散落在系統(tǒng)的各個(gè)角落。這些方法僅僅是傳遞間接的調(diào)用鳞仙,因此與系統(tǒng)的商務(wù)邏輯無關(guān)寇蚊。當(dāng)設(shè)計(jì)師試圖從一張類圖看出總體的架構(gòu)時(shí),這些小的方法會(huì)造成迷惑和困擾棍好。

為了克服狹義的迪米特法則的缺點(diǎn)仗岸,可以使用依賴倒轉(zhuǎn)原則,引入一個(gè)抽象的類型引用“抽象陌生人”對(duì)象借笙,使“某人”依賴于“抽象陌生人”扒怖。換言之,就是將“抽象陌生人”變成朋友业稼。

參考資料

《Java與模式》


個(gè)人介紹:

高廣超:多年一線互聯(lián)網(wǎng)研發(fā)與架構(gòu)設(shè)計(jì)經(jīng)驗(yàn)盗痒,擅長設(shè)計(jì)與落地高可用、高性能低散、可擴(kuò)展的互聯(lián)網(wǎng)架構(gòu)俯邓。

本文首發(fā)在 高廣超的簡(jiǎn)書博客 轉(zhuǎn)載請(qǐng)注明骡楼!

簡(jiǎn)書博客
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市看成,隨后出現(xiàn)的幾起案子君编,更是在濱河造成了極大的恐慌,老刑警劉巖川慌,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異祠乃,居然都是意外死亡梦重,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門亮瓷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來琴拧,“玉大人,你說我怎么就攤上這事嘱支◎拘兀” “怎么了?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵除师,是天一觀的道長沛膳。 經(jīng)常有香客問我,道長汛聚,這世上最難降的妖魔是什么锹安? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮倚舀,結(jié)果婚禮上叹哭,老公的妹妹穿的比我還像新娘。我一直安慰自己痕貌,他們只是感情好风罩,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著舵稠,像睡著了一般超升。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上柱查,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天廓俭,我揣著相機(jī)與錄音,去河邊找鬼唉工。 笑死研乒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的淋硝。 我是一名探鬼主播雹熬,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼宽菜,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了竿报?” 一聲冷哼從身側(cè)響起铅乡,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎烈菌,沒想到半個(gè)月后阵幸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芽世,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年挚赊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片济瓢。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡荠割,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出旺矾,到底是詐尸還是另有隱情蔑鹦,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布箕宙,位于F島的核電站嚎朽,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏扒吁。R本人自食惡果不足惜火鼻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望雕崩。 院中可真熱鬧魁索,春花似錦、人聲如沸盼铁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饶火。三九已至鹏控,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間肤寝,已是汗流浹背当辐。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鲤看,地道東北人缘揪。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親找筝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蹈垢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359