軟件的設(shè)計(jì)模式是前人總結(jié)的顿膨,對(duì)一些特定問(wèn)題的一般化的解決方案翅雏。使用設(shè)計(jì)模式可以最大程度地使不同功能模塊達(dá)到高內(nèi)聚揣云,低耦合棘脐,可復(fù)用的形態(tài)斜筐,另開發(fā)者以一種“正確”的方式實(shí)現(xiàn)那些具有相同特點(diǎn)的需求
三種類型:
所有的設(shè)計(jì)模式主要解決以下3大類問(wèn)題
1.創(chuàng)建型:對(duì)象的創(chuàng)建
2.結(jié)構(gòu)型:對(duì)象的組合
3.行為型:對(duì)象的通信
五大原則與一個(gè)法則:
五大原則:
1.單一職責(zé):一個(gè)類應(yīng)該只承擔(dān)單一的職責(zé),只有一個(gè)引起它變化的原因
2.開閉原則:對(duì)拓展開放蛀缝,對(duì)修改關(guān)閉顷链,確保在后續(xù)代碼變動(dòng)時(shí)原模塊功能不受影響
3.里氏替換原則:子類可以替換父類,拓展了的子類可以生效屈梁。是對(duì)開閉原則的補(bǔ)充
4.接口隔離原則:使用多個(gè)接口嗤练,細(xì)化對(duì)行為的抽象榛了,減少對(duì)無(wú)關(guān)行為的依賴
5.依賴倒轉(zhuǎn)原則:針對(duì)接口編程,使依賴轉(zhuǎn)移到接口上煞抬,減少模塊間耦合忽冻。是開閉原則的基礎(chǔ)
一個(gè)法則:
1.迪米特法則(最少了解法則):不同實(shí)體間應(yīng)該盡可能減少對(duì)其他實(shí)體的了解
23種設(shè)計(jì)模式:
一、創(chuàng)建型:
1.工廠模式:將對(duì)象的創(chuàng)建和使用解耦此疹,隱藏創(chuàng)建的細(xì)節(jié)僧诚,并使用接口抽象對(duì)象
2.抽象工廠:抽象了工廠,用接口指代那些具有關(guān)聯(lián)的工廠蝗碎,進(jìn)一步復(fù)用工廠邏輯
3.單例模式:創(chuàng)建一個(gè)全局對(duì)象湖笨,避免額外的重復(fù)創(chuàng)建、銷毀消耗蹦骑,并且對(duì)此對(duì)象修改將影響整個(gè)程序
例:Spring IOC容器中默認(rèn)的bean構(gòu)造方式
4.建造者:用接口抽象構(gòu)建復(fù)雜對(duì)象需要的參數(shù)慈省,建造者接收這些參數(shù)進(jìn)行一系列復(fù)雜的構(gòu)造動(dòng)作
例:Jackson中自定義序列化器;Java11的HttpClient請(qǐng)求構(gòu)造器
5.原型模式:通過(guò)特定的原型創(chuàng)建它的拷貝眠菇,避免重復(fù)的創(chuàng)建過(guò)程
二边败、結(jié)構(gòu)型
1.適配器模式:為與原模塊不兼容的新對(duì)象提供兼容層,改變了原接口類型
2.橋接模式:抽象類內(nèi)部保存可替換的實(shí)現(xiàn)類的引用捎废,這樣此抽象類可以通過(guò)繼承拓展笑窜,內(nèi)部的抽象引用亦可替換,兩者實(shí)現(xiàn)了解耦
3.過(guò)濾器模式:將“過(guò)濾”的邏輯抽象成接口并提供它的不同實(shí)現(xiàn)登疗,過(guò)濾器的使用者可隨時(shí)更換他們排截。另:Java8中的Lambda使過(guò)濾器的創(chuàng)建變得相當(dāng)方便
例:Stream API中filter操作符的參數(shù)
4.組合模式:使用類內(nèi)部的成員變量關(guān)聯(lián)其他類型,對(duì)外屏蔽了這層關(guān)聯(lián)關(guān)系
5.裝飾器模式:當(dāng)需要拓展原類型功能時(shí)辐益,使用帶有原類型作為成員變量的抽象類代替原類型而不使用繼承拓展原類型断傲,新的拓展類可被靈活繼承
6.外觀模式:使用外觀類封裝復(fù)雜的類關(guān)系,對(duì)外提供單一調(diào)用接口
7.享元模式:共享具有共同屬性的對(duì)象智政,比如緩存已創(chuàng)建的對(duì)象认罩。需要注意封裝緩存邏輯,關(guān)注多線程場(chǎng)景下的并發(fā)問(wèn)題
例:緩存多次RPC調(diào)用獲取到的結(jié)果
8.代理模式:控制對(duì)已有對(duì)象的訪問(wèn)续捂。區(qū)別于適配器模式:不改變被代理類的接口垦垂;區(qū)別于裝飾器模式:用于控制訪問(wèn)而不是拓展
三、行為型
1.責(zé)任鏈模式:將多個(gè)消費(fèi)者封裝起來(lái)成為調(diào)用鏈疾忍,使調(diào)用鏈上指定的消費(fèi)者消費(fèi)數(shù)據(jù)乔外,將消費(fèi)者和生產(chǎn)者解耦
例:對(duì)Go語(yǔ)言中的錯(cuò)誤處理的封裝;Gin框架中context處理鏈
2.命令模式:將部分邏輯封裝作為命令傳輸給接收者一罩,接收者在執(zhí)行命令前杨幼、后可進(jìn)行額外的操作(重試、超時(shí)控制等),解耦了調(diào)用者和接收者
例:Hystrix的使用差购;RxJs中Observable操作符的使用
3.解釋器模式:封裝特定的表達(dá)式四瘫,應(yīng)用較少
4.迭代器模式:封裝通用的遍歷邏輯,將游走元素的責(zé)任分離出來(lái)
例:Java中的Iterator
5.中介者模式:對(duì)于一組“職責(zé)明確”的對(duì)象欲逃,使用一個(gè)中介者來(lái)承擔(dān)他們間的通訊責(zé)任找蜜,所有對(duì)象的通信均依賴這個(gè)中介者而不是相互依賴。
6.備忘錄模式:使用緩存類稳析、管理類維護(hù)同個(gè)對(duì)象在不同時(shí)間的狀態(tài)洗做,緩存類保存狀態(tài),管理類對(duì)緩存類進(jìn)行讀寫操作彰居,備忘錄和客戶端的調(diào)用實(shí)現(xiàn)了解耦
7.觀察者模式:被觀察者內(nèi)儲(chǔ)存多個(gè)觀察者的引用诚纸,一旦被觀察者發(fā)生變化則一次性通知他們。使用一套通知機(jī)制解耦了觀察者和被觀察者
8.狀態(tài)模式:將對(duì)象的狀態(tài)抽象為接口并提取出來(lái)陈惰,對(duì)象內(nèi)部提供更新這個(gè)狀態(tài)抽象和根據(jù)狀態(tài)行動(dòng)的方法
9.空對(duì)象模式:使用一個(gè)預(yù)定義的包含判空信息的對(duì)象來(lái)指代業(yè)務(wù)對(duì)象畦徘,將判空邏輯從業(yè)務(wù)代碼中提取出來(lái),原本的空對(duì)象將對(duì)應(yīng)該模式中的“無(wú)動(dòng)作”
例:Java8提供的Optional類
10.策略模式:一組特定的具有相似輸入輸出行為可以抽象為特定接口的實(shí)現(xiàn)類抬闯,使用“類”來(lái)區(qū)分井辆,使用“接口”來(lái)統(tǒng)一,外部?jī)H通過(guò)接口調(diào)用接口特定實(shí)現(xiàn)而不需要關(guān)注實(shí)現(xiàn)本身
例:重構(gòu)代碼時(shí)溶握,可用特定的接口抽象將原來(lái)具有多個(gè)相似行為的對(duì)象杯缺;推薦算法中特定的計(jì)算公式
11.模板模式:抽象的個(gè)體(如抽象類、接口等)本身可以帶有具體的實(shí)現(xiàn)奈虾,以消除它的其他具體實(shí)現(xiàn)中的模板代碼
例:抽象類中的具體方法夺谁;Java8中的默認(rèn)方法
12.訪客模式:將特定數(shù)據(jù)封裝起來(lái)以控制對(duì)他們的訪問(wèn)廉赔,提供對(duì)數(shù)據(jù)的訪問(wèn)的“訪問(wèn)者”接口肉微,對(duì)數(shù)據(jù)的封裝層將使用訪問(wèn)者消費(fèi)數(shù)據(jù)
例:Gin框架使用HandlerFunc抽象來(lái)消費(fèi)context
一些想法
接口和抽象類
我關(guān)注到了上述這設(shè)計(jì)模式的例子幾乎都使用到了接口和抽象類,這兩者起到了“抽象特定行為”的功能
“高內(nèi)聚蜡塌,低耦合”的結(jié)構(gòu)是應(yīng)用這些模式需要達(dá)到的目標(biāo)碉纳,使用接口指代具體的實(shí)現(xiàn)可以降低不同方法、類間的依賴馏艾,減少耦合劳曹,外部通過(guò)接口這一形式訪問(wèn)對(duì)象也可以屏蔽相關(guān)對(duì)象中的無(wú)關(guān)細(xì)節(jié)
設(shè)計(jì)模式or思考設(shè)計(jì)
正如開篇提到的,這幾十種設(shè)計(jì)模式更多的關(guān)注的是對(duì)象與對(duì)象間的協(xié)同的關(guān)系琅摩,但是各種語(yǔ)言發(fā)展到今天,各種新特性層出不窮,表達(dá)能力也日益增強(qiáng)舒裤,擁有正統(tǒng)OOP血脈的Java也并不將“對(duì)象”作為編碼的起點(diǎn)(例如Java8中加入的Lambda可以另我們將某種行為作為參數(shù)傳遞忍些,在此之前實(shí)現(xiàn)這一目標(biāo)需要新建一個(gè)實(shí)現(xiàn)了抽象接口方法的匿名類的對(duì)象)
與其說(shuō)應(yīng)該在編碼中使用“設(shè)計(jì)模式”不如說(shuō)應(yīng)該應(yīng)該思考如何去設(shè)計(jì)。在學(xué)習(xí)完這幾十種模式和相關(guān)概念后,我回頭看看之前編寫的代碼岖沛,其實(shí)有相當(dāng)一部分已經(jīng)應(yīng)用了某種“模式”暑始,這一切都發(fā)生得自然而然,沒(méi)有刻意也沒(méi)有硬搬
學(xué)習(xí)這些對(duì)我們的代碼結(jié)構(gòu)設(shè)計(jì)工作有很大的啟發(fā)婴削,但是沒(méi)有必要生搬硬套其中的做法到現(xiàn)實(shí)中去廊镜,我們必須得結(jié)合實(shí)際考慮如何去做代碼的設(shè)計(jì)工作