設(shè)計(jì)準(zhǔn)則和設(shè)計(jì)原則

簡(jiǎn)介

在學(xué)習(xí)具體的設(shè)計(jì)模式之前养篓,首先有必要弄清楚我們學(xué)習(xí)的目的之所在抵恋。
設(shè)計(jì)模式(Design pattern)代表了最佳的實(shí)踐碳默,通常被有經(jīng)驗(yàn)的面向?qū)ο蟮能浖_發(fā)人員所采用钱慢。設(shè)計(jì)模式是軟件開發(fā)人員在軟件開發(fā)過程中面臨的一般問題的解決方案逮京。項(xiàng)目中合理地運(yùn)用設(shè)計(jì)模式可以完美地解決很多問題,每種模式在現(xiàn)實(shí)中都有相應(yīng)的原理來與之對(duì)應(yīng)束莫,每種模式都描述了一個(gè)在我們周圍不斷重復(fù)發(fā)生的問題懒棉,以及該問題的核心解決方案,這也是設(shè)計(jì)模式能被廣泛應(yīng)用的原因览绿。
而這些設(shè)計(jì)模式策严,代表的是一種解決問題的思想。我們?cè)诮鉀Q問題和設(shè)計(jì)這些模式的時(shí)候本身也是有原則可循的饿敲,甚至可以制定一些需要強(qiáng)制遵守的準(zhǔn)則妻导。
這里我們不糾結(jié)實(shí)現(xiàn)的語(yǔ)言,當(dāng)然畢竟是前端怀各,盡量使用 es6 或者 ts倔韭。

1. Lnix/Uinux 設(shè)計(jì)準(zhǔn)則

Linux 是一個(gè)偉大的操作系統(tǒng),在深入我們的學(xué)習(xí)之前瓢对,我們來了解一下其設(shè)計(jì)哲學(xué)寿酌。
Linux/Unix 設(shè)計(jì)思想將 Linux 的開發(fā)方式與 Unix 的原理有效地結(jié)合起來,總結(jié)出Linux 與 Unix 軟件開發(fā)中的設(shè)計(jì)原則硕蛹〈继郏《LinuxUnix設(shè)計(jì)思想/圖靈程序設(shè)計(jì)叢書》前8章分別介紹了 Linu x與 Unix 中 9 條基本的哲學(xué)準(zhǔn)則和 10 條次要準(zhǔn)則。

1.1 九大準(zhǔn)則

  • 準(zhǔn)則1:小即是美
  • 準(zhǔn)則2:讓每個(gè)程序只做好一件事
  • 準(zhǔn)則3:快速建立原型
  • 準(zhǔn)則4:舍棄高效率而取可移植性
  • 準(zhǔn)則5:采用純文本來存儲(chǔ)數(shù)據(jù)
  • 準(zhǔn)則6:充分利用軟件的杠桿效應(yīng)(軟件復(fù)用)
  • 準(zhǔn)則7:使用 shell 腳本來提高杠桿效應(yīng)和可移植性
  • 準(zhǔn)則8:避免強(qiáng)制性的用戶界面
  • 準(zhǔn)則9:讓每個(gè)程序都稱為過濾器

準(zhǔn)則1: 小即是美

其實(shí)我一直有把車換成高爾夫 GTI 的沖動(dòng) —— 車小法焰,性能還不錯(cuò)

任何程序的大部分代碼, 實(shí)際上都沒在執(zhí)行它所宣稱的功能.

比如, 一個(gè)復(fù)制文件的小程序, 執(zhí)行步驟往往如下:
(1)輸入源文件地址
(2)檢查源文件是否存在

(4)詢問目標(biāo)文件地址

(10)將源文件的數(shù)據(jù), 復(fù)制到目標(biāo)文件里
(11)關(guān)閉源文件
(12)關(guān)閉目標(biāo)文件

請(qǐng)注意: 文件只在步驟(10)中, 才被真正復(fù)制. 其他步驟的操作, 其實(shí)也可以用在除了文件復(fù)制之外的其他眾多任務(wù)中. 它們是個(gè)公共的流程操作而已.

一個(gè)遵循 Unix 哲學(xué)的程序, 應(yīng)該在調(diào)用時(shí), 就已經(jīng)獲取了有效的源文件地址和目標(biāo)文件地址. 既然不由它自己來獲取, 那么由誰(shuí)來獲取呢? 從 linux 中自帶提供的所有小程序里面秧荆。
眾多小程序, 分別提供了執(zhí)行不同的功能: 獲取文件名, 檢查文件是否存在, 確定其中的內(nèi)容是否為空.

小程序的參考指標(biāo) ---- 可維護(hù)性:
如果你編寫的軟件有這些跡象, 表明已經(jīng)偏離了Unix的操作理念:

  • 傳遞給函數(shù)調(diào)用的參數(shù)數(shù)量過多, 導(dǎo)致代碼超出了一個(gè)屏幕的寬度
  • 子程序代碼的長(zhǎng)度, 超過了整個(gè)屏幕, 或是一張A4紙的長(zhǎng)度
  • 要依靠代碼注釋, 才能記住該子程序到底在做些什么
  • 在獲取目錄列表的時(shí)候, 一個(gè)屏幕已經(jīng)顯示不下這些源文件的名稱
  • 某個(gè)文件已經(jīng)變得很難控制, 無(wú)法再定義程序的全局變量了
  • 你已經(jīng)無(wú)法記住一個(gè)給定的錯(cuò)誤信息, 是在什么條件下引發(fā)的
  • 小程序避免去預(yù)測(cè)未來的情況, 未來, 新的接口會(huì)出現(xiàn), 數(shù)據(jù)格式也將發(fā)展, 程序的互動(dòng)方式, 也會(huì)隨著大家口味的變化而不同. 新硬件技術(shù)出來, 舊算法就會(huì)顯得過時(shí)。

書中講到了小的好處:

  • 易于理解和學(xué)習(xí)埃仪。如果你想要寫出全世界都是用的程序乙濒,那這一點(diǎn)很重要,無(wú)論是大牛還是小白贵试,都能輕松是用琉兜,才能推廣開來。
  • 易于維護(hù)毙玻。即便是自己寫的代碼豌蟋,過半年自己都忘記當(dāng)時(shí)寫的是什么了,要考慮這一點(diǎn)桑滩。
  • 消耗更少的資源梧疲。“小”到制作一件事运准,用多少就消耗多少幌氮,不做一點(diǎn)額外的開銷和浪費(fèi)。
  • 更易于和其他工具結(jié)合胁澳。即可擴(kuò)展性更好该互,符合開放封閉原則。

越是大的程序韭畸,越需要模塊小宇智,函數(shù)小,越是大的企業(yè)胰丁,越需要螺絲釘随橘。

準(zhǔn)則2:讓每個(gè)程序只做好一件事

把準(zhǔn)則 1 做到了極致。越是大型的系統(tǒng)锦庸,這個(gè)原則越重要机蔗,否則越大就越亂。
書中列舉了一個(gè)范例 —— ls命令甘萧。ls本來是很簡(jiǎn)單的一個(gè)命令萝嘁,現(xiàn)在卻搞的有 20 多個(gè)參數(shù),而且正在逐步增加幔嗦。這就使得ls慢慢變成一個(gè)很龐大的命令酿愧,但我們?nèi)粘?90% 的場(chǎng)景都使用它最簡(jiǎn)單的功能。理想的做法是邀泉,ls還保持簡(jiǎn)潔的功能嬉挡,另外開發(fā)新的命令來滿足其他配置參數(shù)實(shí)現(xiàn)的功能。這就例如汇恤,cat可查看全部?jī)?nèi)容庞钢,想看頭或者尾,分別使用 head 和 tail —— 這就分的清晰了因谎。

準(zhǔn)則3:快速建立原型

大教堂與集市基括,我們選擇集市。

軟件不是汽車财岔,它可以每天都迭代更新风皿,它可以今天發(fā)現(xiàn)有問題然后明天修復(fù)過來河爹。而且,誰(shuí)都無(wú)法預(yù)測(cè)它未來將會(huì)怎樣變化桐款,客戶也無(wú)法通過語(yǔ)言清楚的描述他們需要的軟件咸这〔晒唬基于以上所有原因癣防,軟件都需要先有原型殉簸,再不斷慢慢完善畸陡,這也是一個(gè)降低風(fēng)險(xiǎn)步清、慢慢學(xué)習(xí)的過程霍转。

  • 永遠(yuǎn)不要自己猜測(cè)用戶想要的軟件
  • 永遠(yuǎn)不要相信用戶開始時(shí)描述的他們想要的軟件
  • 永遠(yuǎn)不要想一次做完永遠(yuǎn)不改

準(zhǔn)則4:舍棄高效率而取可移植性

越高效的程序往往越不可移植鲁捏,但是好的程序往往會(huì)被移植到各個(gè)地方使用 —— 從這里能看出鹊汛,對(duì)于程序來說朋凉,最重要的是可移植性州丹,而非高效。

至于效率問題杂彭,不用花費(fèi)太多的精力去優(yōu)化当叭,它會(huì)很快因?yàn)橛布母露玫浇鉀Q —— 摩爾定律。

準(zhǔn)則5:采用純文本來存儲(chǔ)數(shù)據(jù)

采用純文本存儲(chǔ)數(shù)據(jù)盖灸,可能沒有二進(jìn)制方式效率高蚁鳖,但是有以下好處:

  • 方便轉(zhuǎn)換格式:所有系統(tǒng)都支持文本編輯,而且文本的編碼規(guī)范赁炎,業(yè)界都是統(tǒng)一標(biāo)準(zhǔn)的醉箕。但是對(duì)于二進(jìn)制 —— 每個(gè)供應(yīng)商都提供了自己的二進(jìn)制編碼,且相互不兼容
  • 易于閱讀和編輯:首先徙垫,文本格式人類一眼就認(rèn)識(shí)讥裤;其次,可通過簡(jiǎn)單的工具進(jìn)行編輯姻报、編輯完直接保存無(wú)需轉(zhuǎn)換己英;第三,文本格式非常適合 linux 中的管道(pipe)操作
  • 易于系統(tǒng)處理:存儲(chǔ)是文本格式吴旋,那么 linux 的標(biāo)準(zhǔn)輸入輸出就可以全部用文本格式损肛,linux 的實(shí)用工具只處理文本格式即可。如grep diff等

雖然效率不佳荣瑟,但是可移植性好治拿,而且易于閱讀和操作,這些都是符合本書其他原則的笆焰。最后劫谅,效率不佳的問題,會(huì)通過明年硬件的升級(jí)而得到解決(摩爾定律)

準(zhǔn)則6:充分利用軟件的杠桿效應(yīng)(軟件復(fù)用)

NIH —— Not Invent Here ,即要借用(或復(fù)用)現(xiàn)有的軟件捏检,而不是重造輪子
這個(gè)道理大家都明白荞驴,但是書中有兩點(diǎn)我覺得很重要:

  • 軟件想要被復(fù)用,就得符合工業(yè)標(biāo)準(zhǔn)贯城。第一戴尸,軟件貢獻(xiàn)者要熟悉標(biāo)準(zhǔn);第二冤狡,軟件使用者也要熟悉標(biāo)準(zhǔn);第三项棠,當(dāng)某個(gè)軟件在業(yè)界還沒有標(biāo)準(zhǔn)的時(shí)候悲雳,要勇敢的去自己制定(如 jQuery)。
  • 更多的人貢獻(xiàn)軟件香追,開源社區(qū)和開源文化很重要合瓢。

準(zhǔn)則7:使用 shell 腳本來提高杠桿效應(yīng)和可移植性

Linux 提倡使用 Shell 腳本,不需要編譯透典,而且可移植性好晴楔。

準(zhǔn)則8:避免強(qiáng)制性的用戶界面

linux 系統(tǒng)中,GUI 都只是一個(gè)普通的軟件而已峭咒,并不是強(qiáng)行和系統(tǒng)綁定的税弃。如果軟件有了強(qiáng)制性的用戶界面,會(huì)帶來各種各樣的問題

  • 強(qiáng)制要求用戶是人類凑队,但是一個(gè)軟件的用戶很可能不是人類
  • 導(dǎo)致軟件龐大则果,占用資源多
  • 擴(kuò)展性差
  • 無(wú)法發(fā)揮杠桿效應(yīng)
  • ……

準(zhǔn)則9:讓每個(gè)程序都成為過濾器

程序不會(huì)創(chuàng)造數(shù)據(jù),只有人類才會(huì)創(chuàng)造數(shù)據(jù)漩氨。因此西壮,每個(gè)程序都僅僅是一個(gè)過濾器而已。

linux 的常用都是過濾器叫惊,例如ls | grep 'README.md'款青,就是找出當(dāng)前目錄下的 README.md文件。其中ls grep都是過濾器霍狰,過濾器就必須有:輸入抡草、輸出。 這其實(shí)正好對(duì)應(yīng)著 linux 的標(biāo)準(zhǔn)輸入輸出(stdio)—— stdin stdout stderr蔗坯。

1.2 十條小準(zhǔn)則

(1)允許用戶定制環(huán)境渠牲。Unix用戶喜歡掌控系統(tǒng)環(huán)境,并且是整個(gè)環(huán)境步悠。很多Unix應(yīng)用程序絕對(duì)不會(huì)一刀切地使用交互風(fēng)格签杈,而是將選擇的權(quán)利交給用戶。它的基本思想就是,程序應(yīng)該只是提供解決問題的機(jī)制答姥,而不是為解決問題的方法限定標(biāo)準(zhǔn)铣除。讓用戶探索屬于自己的通往計(jì)算機(jī)的家境之路吧。

(2)盡量使操作系統(tǒng)內(nèi)核小而輕巧鹦付。盡管對(duì)新功能的追求永無(wú)止境尚粘,Unix開發(fā)人員還是喜歡讓操作系統(tǒng)最核心部分保持最小的規(guī)模。當(dāng)然敲长,他們并不總是能做到這一點(diǎn)郎嫁,但這是他們的目標(biāo)。

(3)使用小寫字母祈噪,并盡量保持簡(jiǎn)短泽铛。使用小寫字母是Unix環(huán)境中的傳統(tǒng),盡管這么做的理由已不復(fù)存在辑鲤,但人們還是保留了這個(gè)傳統(tǒng)盔腔。今天,許多Unix用戶之所以要使用小寫的命令和神秘的名字月褥,不再是因?yàn)橛衅湎拗茥l件弛随,而是他們就喜歡這么做。

(4)保護(hù)樹木宁赤。Unix用戶普遍不太贊成使用紙質(zhì)文檔舀透。而是在線存儲(chǔ)所有文字檔案。此外决左,使用功能強(qiáng)大的在線工具來處理文件是非常環(huán)保的做法盐杂。

(5)沉默是金。在需要提供出錯(cuò)信息的時(shí)候哆窿,Unix命令是出了名的喜歡保持沉默链烈。雖然很多經(jīng)驗(yàn)豐富的Unix用戶認(rèn)為這是可取得做法,可其他操作系統(tǒng)的用戶卻并不贊同這種觀點(diǎn)挚躯。

(6)并行思考强衡。大多數(shù)任務(wù)都能分解成更小的子任務(wù)。這些子任務(wù)可以并行運(yùn)行码荔,因而漩勤,在完成一項(xiàng)大任務(wù)的時(shí)間內(nèi),可以完成更多子任務(wù)缩搅。今天已涌現(xiàn)出大量對(duì)稱處理(symmetric multiprocessing越败,SMP)設(shè)計(jì),這說明計(jì)算機(jī)行業(yè)正朝著并行處理的方向發(fā)展硼瓣。

(7)各部分之和大于整體究飞。小程序集合而成的大型應(yīng)用程序比單個(gè)的大程序更靈活置谦,也更為實(shí)用,本條準(zhǔn)則正式源于此想法亿傅。兩種解決方案可能具備同樣的功能媒峡,可集合小程序的做法更具有前瞻性。

(8)尋找90%的解決方案葵擎。百分百的完成任何事情都是很困難的谅阿。完成90%的目標(biāo)會(huì)更有效率,并且更節(jié)省成本酬滤。Unix開發(fā)人員總是在尋找能夠滿足目標(biāo)用戶90%要求的解決方案签餐,剩下的10%則任其自生自滅。

(9)更壞就是更好盯串。Unix愛好者認(rèn)為具有”最小公分母“的系統(tǒng)是最容易存活的系統(tǒng)氯檐。比起高品質(zhì)而昂貴的系統(tǒng),那些便宜但有效的系統(tǒng)更容易得到普及嘴脾。于是,PC兼容機(jī)的世界從Unix世界借鑒了此想法蔬墩,并取得了巨大成功译打。這其中的關(guān)鍵字就是包容。如果某一事物的包容性強(qiáng)到足以涵蓋幾乎所有事物拇颅,那它就比那些”獨(dú)家”系統(tǒng)要好得多奏司。

(10)層次化思考。Unix用戶和開發(fā)人員都喜歡層次來組織事物樟插。例如韵洋,Unix目錄結(jié)構(gòu)是最早將樹結(jié)構(gòu)應(yīng)用于文件系統(tǒng)的架構(gòu)之一。Unix的層次化思考已擴(kuò)展到其他領(lǐng)域黄锤,如網(wǎng)絡(luò)服務(wù)命名器搪缨、窗口管理、面向?qū)ο箝_發(fā)鸵熟。

2. 面向?qū)ο笤O(shè)計(jì)的七大原則

這里常說的有五大原則:即 SOLID 原則副编,在此之外還有 L 和 C/A 原則,我們一一介紹流强。

2.1 S(Single responsibility principle)——單一職責(zé)原則

2.1.1 定義

所謂職責(zé)是指類變化的原因痹届。如果一個(gè)類有多于一個(gè)的動(dòng)機(jī)被改變,那么這個(gè)類就具有多于一個(gè)的職責(zé)打月。而單一職責(zé)原則就是指一個(gè)類或者模塊應(yīng)該有且只有一個(gè)改變的原因队腐。

2.1.2 理解

不同的類應(yīng)該承擔(dān)不同的職責(zé)。做系統(tǒng)設(shè)計(jì)時(shí)奏篙,如果發(fā)現(xiàn)有一個(gè)類擁有了兩種職責(zé)柴淘,那么就要問一個(gè)問題,這些職責(zé)真的有必要放在一個(gè)類嗎,可以再繼續(xù)拆分么悠就?如果答案是肯定的千绪,就應(yīng)該繼續(xù)拆分。

2.1.3 意義

  • 類的復(fù)雜性降低梗脾,職責(zé)清晰荸型,可讀性和可維護(hù)性提高。
  • 變更引起的風(fēng)險(xiǎn)降低炸茧。變更是必不可少的瑞妇,如果接口的單一職責(zé)做得好,一個(gè)接口修改只對(duì)相應(yīng)的實(shí)現(xiàn)類有影響梭冠,對(duì)其他的接口無(wú)影響辕狰,這對(duì)系統(tǒng)的擴(kuò)展性、維護(hù)性都有非常大的幫助控漠。

2.1.4 示例

class ShoppinCar {
  constructor(){
    this.goods = [];
  }
  addGoods(good){
    this.goods = [good];
  }
  getGoodsList(){
    return this.goods;
  }
}
class Settlement {
  constructor(){
    this.result = 0; 
  }
  calculatePrice(list,key){
    let allPrice = 0;
    list.forEach((el) => {
      allPrice += el[key];
    })
    this.result = allPrice;
  }
  getAllPrice(){
    return this.result;
  }
}

上述代碼中蔓倍, ShoppinCar 類存在兩個(gè)方法 addGoods 和 getGoodsList,分別是添加商品和獲取商品列表盐捷。Settlement 類中存在兩個(gè)方法 calculatePrice 和getAllPrice 分別做的事情是計(jì)算價(jià)錢與獲取總價(jià)錢偶翅。ShoppinCar 與 Settlement 都是在做自己的事情。添加商品與計(jì)算價(jià)格碉渡,雖然在業(yè)務(wù)上是相互依賴的聚谁,但是在代碼中分散在兩個(gè)類,然后他們自己做自己的事情滞诺。其中任何一個(gè)類更改不會(huì)對(duì)另一個(gè)類進(jìn)行更改形导。

2.2 O(Open Closed Principle, OCP)——開閉原則

2.2.1 定義

在面向?qū)ο缶幊填I(lǐng)域中,開閉原則規(guī)定“軟件中的對(duì)象(類习霹,模塊朵耕,函數(shù)等等)應(yīng)該對(duì)于擴(kuò)展是開放的,但是對(duì)于修改是封閉的”淋叶,這意味著一個(gè)實(shí)體是允許在不改變它的源代碼的前提下變更它的行為憔披。

2.2.2 理解

這是所有原則中最核心和基礎(chǔ)的原則,軟件功能發(fā)生變化時(shí)爸吮,應(yīng)該通過擴(kuò)展實(shí)體(類芬膝、模塊、函數(shù)等)行為去適應(yīng)形娇,而不要修改已有代碼锰霜。其核心是封裝變化,封裝變化有兩層含義桐早,第一是將相同的變化封裝到一個(gè)接口或抽象類中癣缅;第二是將不同的變化封裝到不同的接口或抽象類中厨剪。

2.2.3 意義

  • 開閉原則有利于進(jìn)行單元測(cè)試
  • 開閉原則可以提高復(fù)用性
  • 開閉原則可以提高可維護(hù)性
  • 面向?qū)ο箝_發(fā)的要求

2.2.4 實(shí)例

class Teacher {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  teach() {
    console.log('teach students');
  }
  eat() {
    console.log('eat food');
  }
}

class MathsTeacher extends Teacher{
  teach() {
    console.log('teach maths')
  }
}

一個(gè)教師對(duì)象,具有教書的方法友存,現(xiàn)在需要擴(kuò)展一個(gè)專門教數(shù)學(xué)的功能祷膳,那么我們使用繼承并重寫該方法即可。還要實(shí)現(xiàn)其他的教學(xué)方法的話屡立,可以照此繼續(xù)添加直晨。這樣不同的對(duì)象互不影響。

3. L(Liskov Substitution Principle, LSP)——里氏替換原則

3.1 定義

里氏代換原則中說膨俐,任何基類可以出現(xiàn)的地方勇皇,子類一定可以出現(xiàn)。 LSP是繼承復(fù)用的基石焚刺,只有當(dāng)衍生類可以替換掉基類敛摘,軟件單位的功能不受到影響時(shí),基類才能真正被復(fù)用乳愉,而衍生類也能夠在基類的基礎(chǔ)上增加新的行為兄淫。里氏代換原則是對(duì)“開-閉”原則的補(bǔ)充。實(shí)現(xiàn)“開-閉”原則的關(guān)鍵步驟就是抽象化蔓姚。而基類與子類的繼承關(guān)系就是抽象化的具體實(shí)現(xiàn)捕虽,所以里氏代換原則是對(duì)實(shí)現(xiàn)抽象化的具體步驟的規(guī)范。

3.2 理解

假設(shè)我有一個(gè)類是鳥赂乐,定義了一個(gè) fly 的方法薯鳍,可以在天空飛咖气。而我認(rèn)為鴕鳥也是鳥挨措,并且繼承了這個(gè)鳥類。但是我發(fā)現(xiàn)鴕鳥不會(huì)飛崩溪,如果把使用飛鳥的地方替換成鴕鳥浅役,那么程序就會(huì)掛掉。你當(dāng)然也可以在鴕鳥類破壞性地重寫這個(gè) fly 方法伶唯,但這顯然違背了鳥類的設(shè)計(jì)意圖觉既。
定義包含四層意思:
1) 子類可以實(shí)現(xiàn)父類的抽象方法,但不能覆寫父類的非抽象方法乳幸。 父類中凡是已經(jīng)實(shí)現(xiàn)好的方法(相對(duì)于抽象方法而言)瞪讼,實(shí)際上是在設(shè)定一系列的規(guī)范和契約,雖然它不強(qiáng)制要求所有的子類必須遵從這些契約粹断,但是如果子類對(duì)這些非抽象方法任意修改符欠,就會(huì)對(duì)整個(gè)繼承體系造成破壞。
2) 子類中可以增加自己特有的方法瓶埋。
3) 覆寫或?qū)崿F(xiàn)父類的方法時(shí)希柿,輸入?yún)?shù)可以被放大诊沪。
4) 覆寫或?qū)崿F(xiàn)父類的方法時(shí)輸出結(jié)果可以被縮小。
我理解就是推薦重載而不是重寫曾撤,需要重寫的地方端姚,最好是定義一個(gè)抽象方法或者接口,在子類實(shí)現(xiàn)挤悉。當(dāng)然實(shí)際情況下渐裸,使用重寫是不可避免的,但是不要進(jìn)行破壞性的重寫尖啡。
如果子類不能完整地實(shí)現(xiàn)父類的方法橄仆,或者父類的某些方法在子類中已經(jīng)發(fā)生“畸變”,則建議斷開父子繼承關(guān)系衅斩,在兩者基礎(chǔ)上新增一個(gè)更高級(jí)的抽象類盆顾,或者使用依賴、聚集畏梆、組合等關(guān)系代替繼承您宪。

3.3 意義

里氏替換原則為良好的繼承定義了一個(gè)規(guī)范:

  • 代碼共享,減少創(chuàng)建類的工作量奠涌,每個(gè)子類都擁有父類的方法和屬性
  • 提高代碼的重用性
  • 子類可以形似父類宪巨,但是又異于父類。
  • 提高代碼的可擴(kuò)展性溜畅,實(shí)現(xiàn)父類的方法就可以了捏卓。許多開源框架的擴(kuò)展接口都是通過繼承父類來完成。
  • 提高產(chǎn)品或項(xiàng)目的開放性

3.4 實(shí)例

// 定義鳥類
class bird {
  constructor(name){
    this.name = name
  }
  eat() {
    console.log('eat');
  }
}
// 定義會(huì)飛的鳥類
class flyingBird {
  fly() {
    console.log('fly');
  }
}
// 定義不會(huì)飛的鳥類
class runningBird {
  run() {
    console.log('run');
  }
}

// 定義鴕鳥類
class Ostrich extends OrunningBird {
  constructor(name,color) {
    super(name);
    this.color = color
  }
  color(){
    console.log(this.color)
  }
}

鴕鳥類不是直接繼承鳥類慈格,而是從原鳥類中去除 fly 方法怠晴,抽出了一個(gè)更抽象的鳥類,其下有會(huì)飛的鳥類和會(huì)跑的鳥類浴捆,鴕鳥類繼承會(huì)跑的鳥類蒜田。

4. I (Interface Segregation Principle, ISP)——接口獨(dú)立原則

4.1 定義

客戶端不應(yīng)該依賴它不需要的接口。一個(gè)類對(duì)另一個(gè)類的依賴應(yīng)該建立在最小的接口上选泻。

4.2 理解

打個(gè)比方冲粤,注冊(cè)一般需要校驗(yàn)姓名,密碼页眯,動(dòng)態(tài)驗(yàn)證碼√莶叮現(xiàn)在我有個(gè)簡(jiǎn)單的注冊(cè)系統(tǒng),不需要驗(yàn)證碼窝撵。我當(dāng)然也可以依賴上述接口去實(shí)現(xiàn)傀顾,并且將驗(yàn)證碼校驗(yàn)置位空。但是對(duì)于這個(gè)類來講忿族,理論上它就是支持驗(yàn)正碼校驗(yàn)的锣笨,一旦第三方調(diào)用該對(duì)象時(shí)使用了驗(yàn)證碼校驗(yàn)就可能會(huì)出問題蝌矛。
如果我們將上述三個(gè)功能全部都單獨(dú)抽成一個(gè)接口,就方便我們隨時(shí)進(jìn)行組合了错英。這其實(shí)是單一職責(zé)在接口設(shè)計(jì)上的體現(xiàn)入撒。不過實(shí)際設(shè)計(jì)中我們很少說拆的那么細(xì),需要根據(jù)實(shí)際情況設(shè)計(jì)大小合適的接口椭岩∶┐可以根據(jù)如下標(biāo)準(zhǔn)來設(shè)計(jì):
1)一個(gè)接口只服務(wù)于一個(gè)子模塊或業(yè)務(wù)邏輯
2)通過業(yè)務(wù)邏輯壓縮接口中的public方法
3)已被污染的接口盡量去修改,若變更風(fēng)險(xiǎn)大判哥,可用適配器模式進(jìn)行轉(zhuǎn)化處理
4)了解業(yè)務(wù)背景献雅,避免生搬硬套模式。

4.3 意義

  • 避免接口污染
  • 提高靈活性
  • 提供定制服務(wù)
  • 實(shí)現(xiàn)高內(nèi)聚

4.4 實(shí)例

interface simpleRegister {
    validName(): boolean;
    validPwd(): boolean;
}

interface IVerifycode {
    validVerifyCode(): boolean;
}

class Register  implements simpleRegister, IVerifycode {
    constructor() {
        //
    }
    validName() {
        //
    }
    validPwd() {
        //
    }
    validVerifyCode() {
        //
    }
}

5. D(Dependence Inversion Principle, DIP)——依賴倒置原則

5.1 定義

依賴倒置原則(Dependence Inversion Principle)是程序要依賴于抽象接口塌计,不要依賴于具體實(shí)現(xiàn)挺身。簡(jiǎn)單的說就是要求對(duì)抽象進(jìn)行編程,不要對(duì)實(shí)現(xiàn)進(jìn)行編程锌仅,這樣就降低了客戶與實(shí)現(xiàn)模塊間的耦合章钾。

5.2 理解

駕駛員開車,車會(huì)跑起來热芹,至于這輛車是怎么跑的贱傀,兩個(gè)輪子還是四個(gè)輪子,兩驅(qū)還是四驅(qū)伊脓,駕駛員是不需要去關(guān)注的府寒,這是每個(gè)車自己去關(guān)注和實(shí)現(xiàn)的。

5.3 意義

  • 通過依賴于接口报腔,隔離了具體實(shí)現(xiàn)類
  • 底層的變動(dòng)并不會(huì)導(dǎo)致高層的變動(dòng)
  • 提高了代碼的容錯(cuò)性株搔、擴(kuò)展性和可維護(hù)性

5.4 實(shí)例

// 錯(cuò)誤實(shí)例,一旦這個(gè)駕駛員新增了車輛種類榄笙,就又要改變依賴和實(shí)現(xiàn)邪狞。這是因?yàn)?driver 類依賴了底層的具體車型類祷蝌,關(guān)注了應(yīng)該只由底層去關(guān)注的 run 方法具體實(shí)現(xiàn)茅撞。
class BenzCar {
    run() {
        console.log('benz run');
    }
}
class BmwCar {
    run() {
        console.log('bmw run');
    }
}
class Driver {
    private carType;
    private benzCar;
    private bmwCar;
    
    constructor(carType) {
        this.carType = carType;
        this.benzCar = new BenzCar();
        this.bmwCar = new BmwCar();
    }

    drive() {
        switch (this.carType) {
            case 'benz':
                this.benzCar.run();
                return;
            case 'bmw':
                this.bmwCar.run();
                return;
        }
    }
}
// 正確實(shí)現(xiàn),駕駛員類依賴的是所有車的抽象接口
interface Icar {
    run(): void;
}
class Driver {
    private car;
    constructor(car: Icar) {
        this.car = car;
    }
    drive() {
        this.car.run();
    }
}

6. L(Law of Demeter, LoD)——迪米特法則

6.1 定義

迪米特法則(Law of Demeter)又叫作最少知識(shí)原則(Least Knowledge Principle 簡(jiǎn)寫LKP)巨朦,一個(gè)類對(duì)于其他類知道的越少越好米丘,就是說一個(gè)對(duì)象應(yīng)當(dāng)對(duì)其他對(duì)象有盡可能少的了解,只和朋友通信,不和陌生人說話糊啡。英文簡(jiǎn)寫為: LoD拄查。

6.2 理解

顧客向廚師點(diǎn)菜,廚師要準(zhǔn)備材料棚蓄,配料堕扶,制作碍脏,但顧客不關(guān)心你具體用的哪些材料,配料稍算,也不關(guān)心你先準(zhǔn)備材料還是配料典尾,他不需要關(guān)心任何制作的細(xì)節(jié),他只想吃到自己點(diǎn)的菜糊探。作為廚師钾埂,也不需要顧客來關(guān)心這些,這都是他自己的私有方法科平,愛怎么做怎么做褥紫,只要最后能給到用戶他點(diǎn)的菜即可。

一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象有最少的了解瞪慧,一個(gè)類只需要知道自己需要耦合或者調(diào)用類的public方法即可髓考。
盡量保證風(fēng)險(xiǎn)的不擴(kuò)散,修改的地方越少弃酌,代碼就越好绳军。
一個(gè)類公開的public方法越多,修改時(shí)涉及的面也越大矢腻,變更的風(fēng)險(xiǎn)也越大门驾。

出現(xiàn)在成員變量、方法的輸入輸出參數(shù)中的類稱為成員朋友類多柑,而出現(xiàn)在方法體內(nèi)部的類不屬于朋友類奶是,迪米特法則告訴我們,一個(gè)類只和他的朋友類做交流竣灌,老師和體育委員交流聂沙、體育委員和學(xué)生交流。
在實(shí)際中如果遇到初嘹,一個(gè)方法放在本類中也可以及汉,放在其他類中也合適,那么你可以堅(jiān)持這樣一個(gè)原則:如果一個(gè)方法放在本類中屯烦,既不增加類間關(guān)系坷随,也 對(duì)本類不產(chǎn)生負(fù)面影響,那就放置在本類中驻龟。

迪米特法則的核心觀念就是類間的解耦温眉,弱耦合。但是也要衡量翁狐,既要讓結(jié)構(gòu)清晰类溢,又要高內(nèi)聚低耦合。

6.3 意義

  • 減少對(duì)象之間的耦合性

6.4 實(shí)例

class Cooker {
    constructor(name) {
        this.name = name;
    }

    // 準(zhǔn)備材料
    private prepareMaterial(dishName) {
        //
    }
    // 準(zhǔn)備配料
    private prepareIngredients(dishName) {
        //
    }
    // 制作
    private cook(dishName) {
        //
    }
    // 出品
    out(dishName) {
        this.prepareMaterial(dishName);
        this.prepareIngredients(dishName);
        this.cook(dishName);
    }
}
class Guest {
    constructor(name) {
        this.name = name;
    }

    // 點(diǎn)菜
    order(cooker: Cooker, dishName: string) {
        cooker.out(dishName);
    }
}

7. C/A(Composite/Aggregate Reuse Principle, C/ARP)——組合/聚合復(fù)用原則

7.2 定義

在面向?qū)ο蟮脑O(shè)計(jì)中露懒,如果直接繼承基類闯冷,會(huì)破壞封裝砂心,因?yàn)槔^承將基類的實(shí)現(xiàn)細(xì)節(jié)暴露給子類;如果基類的實(shí)現(xiàn)發(fā)生改變蛇耀,則子類的實(shí)現(xiàn)也不得不發(fā)生改變计贰;從基類繼承而來的實(shí)現(xiàn)是靜態(tài)的,不可能在運(yùn)行時(shí)發(fā)生改變蒂窒,沒有足夠的靈活性躁倒。于是就提出了組合/聚合復(fù)用原則,也就是在實(shí)際開發(fā)設(shè)計(jì)中洒琢,盡量使用合成/聚合秧秉,不要使用類繼承。即在一個(gè)新的對(duì)象里面使用一些已有的對(duì)象衰抑,使之成為新對(duì)象的一部分象迎,新對(duì)象通過向這些對(duì)象的委派達(dá)到復(fù)用已有功能的目的。就是說要盡量的使用合成和聚合呛踊,而不是繼承關(guān)系達(dá)到復(fù)用的目的砾淌。

7.3 理解

這么說吧,你要造車谭网,你可以自己去建造發(fā)動(dòng)機(jī)汪厨,車身,輪子等愉择,也可以直接在外面定做好這些劫乱,然后在車間去組裝它,哪一個(gè)更容易呢锥涕?利用繼承去實(shí)現(xiàn)一個(gè)功能衷戈,就好比你要給家庭做清潔,你或者你的父親必須有一個(gè)人擁有這個(gè)技能层坠,如果你還想做飯殖妇,你們還得去學(xué),如果你們現(xiàn)在要裝修房子破花,你還得學(xué)習(xí)木工谦趣。這樣的話成本是巨大的,不如干脆請(qǐng)保潔旧乞,廚師和木工來做專業(yè)的事情蔚润。你要開公司磅氨,就聚合一幫現(xiàn)成的人來干尺栖,比你從頭學(xué)習(xí)所有技能,一個(gè)人來做肯定快得多烦租,也好的多延赌。
其實(shí)說了這些除盏,就是表達(dá)一個(gè)意思,有現(xiàn)成的挫以,你就用者蠕,不要啥都自己干。這里也簡(jiǎn)單為大家普及一下對(duì)象之間的關(guān)系:
繼承:對(duì)于類來說掐松,這種關(guān)系叫做繼承踱侣,對(duì)于接口來說,這種關(guān)系叫做實(shí)現(xiàn)大磺。繼承是一種“is-a”關(guān)系抡句。
依賴:依賴簡(jiǎn)單的理解,就是一個(gè)類A中的方法使用到了另一個(gè)類B杠愧。這種使用關(guān)系是具有偶然性的待榔、臨時(shí)性的、非常弱的流济,但是B類的變化會(huì)影響到A锐锣。在前面鎖具的例子中,我們有一個(gè)駕駛員開車的例子绳瘟,這就是一種依賴雕憔,駕駛員 drive 的使用會(huì)實(shí)際調(diào)用 car 的 run 方法。car 的 run 方法修改會(huì)影響到駕駛員糖声,但是駕駛員如果一旦不 drive橘茉,兩者之間的關(guān)系就斷開了。一般而言姨丈,依賴關(guān)系在 TS 中體現(xiàn)為局域變量畅卓、方法的形參,或者對(duì)靜態(tài)方法的調(diào)用蟋恬。
關(guān)聯(lián):關(guān)聯(lián)體現(xiàn)的是兩個(gè)類翁潘、或者類與接口之間語(yǔ)義級(jí)別的一種強(qiáng)依賴關(guān)系。被關(guān)聯(lián)類B以類屬性的形式出現(xiàn)在關(guān)聯(lián)類A中歼争,或者關(guān)聯(lián)類A引用了一個(gè)類型為被關(guān)聯(lián)類B的全局變量的這種關(guān)系拜马,就叫關(guān)聯(lián)關(guān)系。就比如說某個(gè)司機(jī)擁有某良特定的車沐绒,車和司機(jī)就產(chǎn)生了關(guān)聯(lián)俩莽。在 TS 中,關(guān)聯(lián)關(guān)系一般使用成員變量來實(shí)現(xiàn)乔遮。
這種關(guān)系比依賴更強(qiáng)扮超、不存在依賴關(guān)系的偶然性、關(guān)系也不是臨時(shí)性的,一般是長(zhǎng)期性的出刷,而且雙方的關(guān)系一般是平等的璧疗、關(guān)聯(lián)可以是單向、雙向的馁龟。
聚合:聚合表示整體與部分的關(guān)系崩侠,部分可以脫離整體作為獨(dú)立個(gè)體存在,即 ‘has-a’ 的關(guān)系坷檩。在代碼層面却音,聚合和關(guān)聯(lián)關(guān)系是一致的,只能從語(yǔ)義級(jí)別來區(qū)分矢炼。普通的關(guān)聯(lián)關(guān)系中僧家,A 類和B 類沒有必然的聯(lián)系,而聚合中裸删,需要 B 類是 A 類的一部分八拱,是一種 ‘has-a’ 的關(guān)系,即 A has-a B; 比如家庭有孩子涯塔,屋子里有院子肌稻,班級(jí)有學(xué)生,雁群有大雁匕荸。但是爹谭,has 不是 must has,A 可以有B榛搔,也可以沒有诺凡。A 是整體, 是部分践惑,整體與部分之間是可分離的腹泌,他們可以具有各自的生命周期,走了一兩只大雁尔觉,雁群并沒有影響凉袱。雁群消失,大雁也還可以存活侦铜。部分可以屬于多個(gè)整體對(duì)象专甩,也可以為多個(gè)整體對(duì)象共享,比如兩家可以共享一個(gè)院子钉稍。
不同于關(guān)聯(lián)關(guān)系的平等地位涤躲,聚合關(guān)系中兩個(gè)類的地位是不平等。
組合:組合也是關(guān)聯(lián)關(guān)系的一種特例贡未,他體現(xiàn)的是一種 ‘contains-a’ 的關(guān)系种樱,這種關(guān)系比聚合更強(qiáng)蒙袍,也稱為強(qiáng)聚合。組合同樣體現(xiàn)整體與部分間的關(guān)系缸托,但此時(shí)整體與部分是不可分的左敌,部分不可作為獨(dú)立個(gè)體單獨(dú)存在瘾蛋,部分的生命周期不能超過整體的生命周期俐镐,整體的生命周期結(jié)束也就意味著部分的生命周期結(jié)束。心臟是人體組成的一部分哺哼,發(fā)動(dòng)機(jī)是車組成的一部分佩抹。
組合關(guān)系中,兩個(gè)類關(guān)系也是不平等的取董。
只看代碼棍苹,是無(wú)法區(qū)分關(guān)聯(lián),聚合和組合的茵汰,具體是哪一種關(guān)系枢里,只能從語(yǔ)義級(jí)別來區(qū)分。較真來講蹂午,心臟可以移植栏豺,發(fā)動(dòng)機(jī)也可以換一輛車?yán)^續(xù)工作,這個(gè)時(shí)候豆胸,你可以將其視作一種聚合奥洼。
理解他們無(wú)須太刻意,視具體情況而定晚胡。比如你倆本來可能只是互有關(guān)聯(lián)的同學(xué)灵奖,后來聚合成了男女盆友,最后組合成了一個(gè)家庭估盘,密不可分瓷患。這個(gè)關(guān)系是層層遞進(jìn)的,理解這一點(diǎn)就好遣妥。
所以它們之間尉尾,尤其是組合和聚合,大多時(shí)候不必分得太清晰燥透,畢竟代碼形式上表現(xiàn)是一樣的沙咏。

組合和聚合均是關(guān)聯(lián)的特殊情況。聚合用來表示“擁有”關(guān)系或者整體與部分的關(guān)系班套;而組合則用來表示一種強(qiáng)得多的“擁有”關(guān)系肢藐。在一個(gè)組合關(guān)系里面,部分和整體的生命周期是一樣的吱韭。一個(gè)組合的新的對(duì)象完全擁有對(duì)其組成部分的支配權(quán)吆豹,包括它們的創(chuàng)建和銷毀等鱼的。使用程序語(yǔ)言的術(shù)語(yǔ)來說,組合而成的新對(duì)象對(duì)組成部分的內(nèi)存分配痘煤、內(nèi)存釋放有絕對(duì)的責(zé)任凑阶。要正確的選擇組合/聚合復(fù)用和繼承,必須透徹地理解里氏替換原則和Coad法則衷快。(Coad法則由Peter Coad提出宙橱,總結(jié)了一些什么時(shí)候使用繼承作為復(fù)用工具的條件。Coad法則:只有當(dāng)以下Coad條件全部被滿足時(shí)蘸拔,才應(yīng)當(dāng)使用繼承關(guān)系)
(1).子類是基類的一個(gè)特殊種類师郑,而不是基類的一個(gè)角色。區(qū)分“Has-A”和“Is-A”调窍。只有“Is-A”關(guān)系才符合繼承關(guān)系宝冕,“Has-A”關(guān)系應(yīng)當(dāng)用聚合來描述。
(2).永遠(yuǎn)不會(huì)出現(xiàn)需要將子類換成另外一個(gè)類的子類的情況邓萨。如果不能肯定將來是否會(huì)變成另外一個(gè)子類的話地梨,就不要使用繼承。
(3).子類具有擴(kuò)展基類的責(zé)任缔恳,而不是具有置換掉(override)或注銷掉(Nullify)基類的責(zé)任宝剖。如果一個(gè)子類需要大量的置換掉基類的行為,那么這個(gè)類就不應(yīng)該是這個(gè)基類的子類褐耳。
(4).只有在分類學(xué)角度上有意義時(shí)诈闺,才可以使用繼承。不要從工具類繼承铃芦。

7.3 意義

詳細(xì)解析可以參見java 依賴雅镊、組合、聚合與繼承
繼承的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):

  • 子類能自動(dòng)繼承父類的接口
  • 創(chuàng)建子類的對(duì)象時(shí)刃滓,無(wú)須創(chuàng)建父類的對(duì)象
    缺點(diǎn):
  • 破壞封裝仁烹,子類與父類之間緊密耦合,子類依賴于父類的實(shí)現(xiàn)咧虎,子類缺乏獨(dú)立性
  • 支持?jǐn)U展卓缰,但是往往以增加系統(tǒng)結(jié)構(gòu)的復(fù)雜度為代價(jià)
  • 不支持動(dòng)態(tài)繼承。在運(yùn)行時(shí)砰诵,子類無(wú)法選擇不同的父類
  • 子類不能改變父類的接口

組合/聚合的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):

  • 不破壞封裝征唬,整體類與局部類之間松耦合,彼此相對(duì)獨(dú)立
  • 具有較好的可擴(kuò)展性
  • 支持動(dòng)態(tài)組合茁彭。在運(yùn)行時(shí)总寒,整體對(duì)象可以選擇不同類型的局部對(duì)象
  • 整體類可以對(duì)局部類進(jìn)行包裝,封裝局部類的接口理肺,提供新的接口
    缺點(diǎn):
  • 整體類不能自動(dòng)獲得和局部類同樣的接口
  • 創(chuàng)建整體類的對(duì)象時(shí)摄闸,需要?jiǎng)?chuàng)建所有局部類的對(duì)象

7.4 實(shí)例

車由引擎善镰,車身和輪子組成,如果我們使用多繼承的方式去實(shí)現(xiàn)一輛車年枕,如下:

interface IEngine {
    activate(): void;
}

interface IBody {
    color: string;
}

interface IWheel {
    roll(): void;
}

class Car implements IEngine, IBody, IWheel {
    private color: string;

    constructor(color) {
        this.color = color;
    }

    activate(): void {
        // 機(jī)械式點(diǎn)火
        console.log('Mechanical ignition');
    }

    roll(): void {
        console.log('wheel roll');
    }

    run(): void {
        this.activate();
        this.roll();
    }
}

我們需要在 car 類中去實(shí)現(xiàn)這些抽象接口炫欺,一旦我們的接口或者實(shí)現(xiàn)發(fā)生變化,比如這里我們將引擎的機(jī)械式打火升級(jí)為電子打火熏兄,那么我們就要在 car 類中去修改其實(shí)現(xiàn)品洛。違反了開閉原則。
而如果我們使用組合方式去實(shí)現(xiàn)霍弹,如下:

class Car {
    private engine: IEngine;
    private body: IBody;
    private wheel: IWheel
    constructor(engine: IEngine, body: IBody, wheel: IWheel) {
        this.engine = engine;
        this.body = body;
        this.wheel = this.wheel;
    }

    run(): void {
        this.engine.activate();
        this.wheel.roll();
    }
}

當(dāng)傳入的 engine 是實(shí)現(xiàn)了電子打火的引擎毫别,即可滿足升級(jí)后的功能娃弓,而我們的 car 完全無(wú)需做任何修改典格。

8. 小結(jié)

事實(shí)上 js 是天然多態(tài)的,沒有抽象台丛,重寫也非常方便耍缴,這種優(yōu)勢(shì)帶來了編程的極簡(jiǎn)體驗(yàn),也產(chǎn)生了理解和維護(hù)難的副作用挽霉。所以在使用 oop 設(shè)計(jì)時(shí)建議用 ts 來代替 js 進(jìn)行編程防嗡。
另外,理解對(duì)象之間的關(guān)系 - 繼承侠坎,依賴蚁趁,關(guān)聯(lián),聚合实胸,組合他嫡,以及面向?qū)ο笤O(shè)計(jì)的核心原則 - 開閉原則,我們才能更好地理解設(shè)計(jì)模式產(chǎn)生的意義庐完,并且據(jù)此設(shè)計(jì)出更合理的類和接口钢属。

參考

設(shè)計(jì)模式 | 菜鳥教程
Linux/Unix 系統(tǒng)設(shè)計(jì)的九大準(zhǔn)則
<linux / unix 設(shè)計(jì)哲學(xué)> 筆記
《Linux/Unix設(shè)計(jì)思想》隨筆 ——Linux/Unix哲學(xué)概述
javascript設(shè)計(jì)模式與六大原則
web前端進(jìn)階之js設(shè)計(jì)模式之設(shè)計(jì)原則篇
Java設(shè)計(jì)模式-六大原則
js 面向?qū)ο笃叽笤瓌t
JavaScript面向?qū)ο笾叽蠡驹瓌t實(shí)例詳解
百度百科-單一職責(zé)原則
百度百科-開閉原則
百度百科-里氏替換原則
百度百科-里氏代換原則
百度百科-接口隔離原則
六大設(shè)計(jì)原則之四:接口隔離原則
百度百科-迪米特法則
java 依賴、組合门躯、聚合與繼承
七個(gè)原則7-合成(組合)/聚合復(fù)用原則
聚合淆党,組合,繼承的區(qū)別
聚合讶凉、組合的區(qū)別
Java學(xué)習(xí)筆記(二)--組合與繼承
為什么組合優(yōu)于繼承染乌?
設(shè)計(jì)模式---->組合/聚合復(fù)用原則
組合/聚合復(fù)用原則詳解--七大面向?qū)ο笤O(shè)計(jì)原則(7)
合成聚合復(fù)用原則
組合/聚合復(fù)用原則

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市懂讯,隨后出現(xiàn)的幾起案子荷憋,更是在濱河造成了極大的恐慌,老刑警劉巖域醇,帶你破解...
    沈念sama閱讀 222,946評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件距淫,死亡現(xiàn)場(chǎng)離奇詭異界赔,居然都是意外死亡嫁怀,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,336評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門酪呻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人盐须,你說我怎么就攤上這事玩荠。” “怎么了贼邓?”我有些...
    開封第一講書人閱讀 169,716評(píng)論 0 364
  • 文/不壞的土叔 我叫張陵阶冈,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我塑径,道長(zhǎng)女坑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,222評(píng)論 1 300
  • 正文 為了忘掉前任统舀,我火速辦了婚禮匆骗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘誉简。我一直安慰自己碉就,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,223評(píng)論 6 398
  • 文/花漫 我一把揭開白布闷串。 她就那樣靜靜地躺著瓮钥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪烹吵。 梳的紋絲不亂的頭發(fā)上碉熄,一...
    開封第一講書人閱讀 52,807評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音年叮,去河邊找鬼具被。 笑死,一個(gè)胖子當(dāng)著我的面吹牛只损,可吹牛的內(nèi)容都是我干的一姿。 我是一名探鬼主播,決...
    沈念sama閱讀 41,235評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼跃惫,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼叮叹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起爆存,我...
    開封第一講書人閱讀 40,189評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蛉顽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后先较,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體携冤,經(jīng)...
    沈念sama閱讀 46,712評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡悼粮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,775評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了曾棕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扣猫。...
    茶點(diǎn)故事閱讀 40,926評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖翘地,靈堂內(nèi)的尸體忽然破棺而出申尤,到底是詐尸還是另有隱情,我是刑警寧澤衙耕,帶...
    沈念sama閱讀 36,580評(píng)論 5 351
  • 正文 年R本政府宣布昧穿,位于F島的核電站,受9級(jí)特大地震影響橙喘,放射性物質(zhì)發(fā)生泄漏时鸵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,259評(píng)論 3 336
  • 文/蒙蒙 一渴杆、第九天 我趴在偏房一處隱蔽的房頂上張望寥枝。 院中可真熱鬧宪塔,春花似錦磁奖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,750評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至南誊,卻和暖如春身诺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背抄囚。 一陣腳步聲響...
    開封第一講書人閱讀 33,867評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工霉赡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人幔托。 一個(gè)月前我還...
    沈念sama閱讀 49,368評(píng)論 3 379
  • 正文 我出身青樓穴亏,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親重挑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子嗓化,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,930評(píng)論 2 361