I. 引言
設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過(guò)分類(lèi)的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。
使用設(shè)計(jì)模式的目的:為了代碼可重用性、讓代碼更容易被他人理解、保證代碼可靠性。 設(shè)計(jì)模式使代碼編寫(xiě)真正工程化;設(shè)計(jì)模式是軟件工程的基礎(chǔ),如同大廈的結(jié)構(gòu)一樣。
創(chuàng)建型模式:對(duì)類(lèi)的實(shí)例化過(guò)程的抽象。一些系統(tǒng)在創(chuàng)建對(duì)象時(shí),需要?jiǎng)討B(tài)地決定怎樣創(chuàng)建對(duì)象,創(chuàng)建哪些對(duì)象,以及如何組合和表示這些對(duì)象。創(chuàng)建模式描述了怎樣構(gòu)造和封裝這些動(dòng)態(tài)的決定。包含類(lèi)的創(chuàng)建模式和對(duì)象的創(chuàng)建模式。
結(jié)構(gòu)型模式:描述如何將類(lèi)或?qū)ο蠼Y(jié)合在一起形成更大的結(jié)構(gòu)。分為類(lèi)的結(jié)構(gòu)模式和對(duì)象的結(jié)構(gòu)模式。類(lèi)的結(jié)構(gòu)模式使用繼承把類(lèi),接口等組合在一起,以形成更大的結(jié)構(gòu)。類(lèi)的結(jié)構(gòu)模式是靜態(tài)的。對(duì)象的結(jié)構(gòu)模式描述怎樣把各種不同類(lèi)型的對(duì)象組合在一起,以實(shí)現(xiàn)新的功能的方法。對(duì)象的結(jié)構(gòu)模式是動(dòng)態(tài)的。
行為型模式:對(duì)在不同的對(duì)象之間劃分責(zé)任和算法的抽象化。不僅僅是關(guān)于類(lèi)和對(duì)象的,并是關(guān)于他們之間的相互作用。類(lèi)的行為模式使用繼承關(guān)系在幾個(gè)類(lèi)之間分配行為。對(duì)象的行為模式則使用對(duì)象的聚合來(lái)分配行為。
設(shè)計(jì)模式分為三種類(lèi)型,共23種。
創(chuàng)建型模式:?jiǎn)卫J?、抽象工廠(chǎng)模式、建造者模式、工廠(chǎng)模式、原型模式。
結(jié)構(gòu)型模式:適配器模式、橋接模式、裝飾模式、組合模式、外觀(guān)模式、享元模式、代理模式。
行為型模式:模版方法模式、命令模式、迭代器模式、觀(guān)察者模式、中介者模式、備忘錄模式、解釋器模式、狀態(tài)模式、策略模式、責(zé)任鏈模式、訪(fǎng)問(wèn)者模式。

下面將對(duì)一些常用的設(shè)計(jì)模式和軟件設(shè)計(jì)的六大原則進(jìn)行介紹。
一. 創(chuàng)建型模式
1. Factory method 工廠(chǎng)方法(工廠(chǎng)模式)
作用
一個(gè)類(lèi)需要一個(gè)產(chǎn)品來(lái)完成某項(xiàng)工作,但它不能確定,也不關(guān)心具體拿到什么產(chǎn)品,因此它定義一個(gè)工廠(chǎng)方法,將具體產(chǎn)品的生產(chǎn)延遲到子類(lèi)決定。
實(shí)現(xiàn)

- 父類(lèi)可以選擇為工廠(chǎng)方法提供一個(gè)默認(rèn)的實(shí)現(xiàn);
- 工廠(chǎng)方法通常在模板方法(Template method)中被調(diào)用,上圖中
AnOperation()就是一個(gè)模板方法。
2. Abstract factory 抽象工廠(chǎng)
作用
系統(tǒng)有一組相互關(guān)聯(lián)的產(chǎn)品接口,及幾套不同的實(shí)現(xiàn)。客戶(hù)只依賴(lài)產(chǎn)品接口,并需要能靈活地在幾套實(shí)現(xiàn)中切換。
因此提供一個(gè)抽象工廠(chǎng)生產(chǎn)抽象的產(chǎn)品,每個(gè)產(chǎn)品在其中都對(duì)應(yīng)一個(gè)工廠(chǎng)方法,產(chǎn)品族的每一套實(shí)現(xiàn)都提供一個(gè)具體工廠(chǎng)??蛻?hù)通過(guò)抽象工廠(chǎng)獲取產(chǎn)品,當(dāng)需要切換到產(chǎn)品的其他實(shí)現(xiàn)時(shí)只需要更換工廠(chǎng)的實(shí)現(xiàn)類(lèi)。
實(shí)現(xiàn)

應(yīng)用
根據(jù)底層數(shù)據(jù)源的不同,DAO的實(shí)現(xiàn)通常有幾套,當(dāng)切換數(shù)據(jù)源時(shí),系統(tǒng)使用的DAO的實(shí)現(xiàn)也應(yīng)當(dāng)能快速切換。這是使用抽象工廠(chǎng)的一個(gè)典型場(chǎng)景。
3. Singleton 單例
作用
保證一個(gè)類(lèi)只有一個(gè)對(duì)象
實(shí)現(xiàn)
-
private構(gòu)造器 -
private static類(lèi)變量 singleton -
public static類(lèi)方法getInstance()返回singleton。
實(shí)例化時(shí)機(jī):
- eager
- lazy
lazy init 多線(xiàn)程問(wèn)題的解決辦法:Double Check
private volatile static A singleton = null;
public static A getInstance(){
if(singleton == null){
sychronized(A.class){
if(singleton == null) singleton = new A();
}
}
return singleton;
}
private A(){}
- 為什么要第二次的null判斷?
在第一次判null / 獲取鎖之間可能有其他線(xiàn)程實(shí)例化了。 - 為什么要
volatile?
在上面提到的情況下,如果沒(méi)有volatile保證的可見(jiàn)性,在第二次null判斷時(shí)當(dāng)前線(xiàn)程可能看不到別的線(xiàn)程創(chuàng)建的對(duì)象,從而通過(guò)并再創(chuàng)建一次。
static 內(nèi)部類(lèi)利用 “類(lèi)的加載/static塊是線(xiàn)程安全的” 實(shí)現(xiàn)線(xiàn)程安全的lazy init:
public class A{
private static class Holder{
private static A singleton = new A();
}
public static A getInstance(){
return Holder.singleton;
}
private A(){}
}
4. Builder 建造者模式
作用
你有一個(gè)產(chǎn)品,該產(chǎn)品由若干part裝配而成,裝配的邏輯是固定的,但各個(gè)part的構(gòu)造是可切換選擇的,Builder模式將 固定的裝配邏輯 與 易變的part構(gòu)造邏輯 分離開(kāi),可以方便地在不同的part實(shí)現(xiàn)邏輯之間切換。
實(shí)現(xiàn)

-
Director#construct()負(fù)責(zé)固定的裝配邏輯; - 一個(gè)
Builder實(shí)例負(fù)責(zé)一個(gè)產(chǎn)品內(nèi)部所有part的構(gòu)造(buildPart()方法族),并向外部暴露方法,在part都裝配完畢后獲取該產(chǎn)品。
交互

5. Prototype 原型模式
作用
用原型實(shí)例指定創(chuàng)建對(duì)象的種類(lèi),并且通過(guò)拷貝這些原型創(chuàng)建新的對(duì)象。
適用性
當(dāng)要實(shí)例化的類(lèi)是在運(yùn)行時(shí)刻指定時(shí),例如,通過(guò)動(dòng)態(tài)裝載;
為了避免創(chuàng)建一個(gè)與產(chǎn)品類(lèi)層次平行的工廠(chǎng)類(lèi)層次時(shí);
當(dāng)一個(gè)類(lèi)的實(shí)例只能有幾個(gè)不同狀態(tài)組合中的一種時(shí)。建立相應(yīng)數(shù)目的原型并克隆它們可能比每次用合適的狀態(tài)手工實(shí)例化該類(lèi)更方便一些。
實(shí)現(xiàn)

二. 結(jié)構(gòu)型模式
1. Adapter 適配器
作用
在兩個(gè)不兼容的接口之間加一個(gè)中間層,用組合的方式將一個(gè)現(xiàn)有對(duì)象匹配到需要的接口。
Convert the interface of a class into another interface the client expects
實(shí)現(xiàn)

2. Proxy 代理
作用
Provide a surrogate or place holder for another object to control access to it
你有一個(gè)真正干活的對(duì)象RealSubject,但需要向client控制對(duì)他的訪(fǎng)問(wèn),比如權(quán)限的控制 / Lazy load / 結(jié)果的緩存等等,因此在client和RealSubject之間增加一個(gè)中間層Proxy代替RealSubject,Proxy包裹RealSubject,將具體功能實(shí)現(xiàn)委托給它,并在RealSubject執(zhí)行真正的功能前后插入自己的邏輯;此外,Proxy向client隱藏了RealSubject的存在。
實(shí)現(xiàn)

與Decorator模式的區(qū)別
Proxy與Decorator有著相似的結(jié)構(gòu),** 他們都在client和真實(shí)對(duì)象之間增加一個(gè)與真實(shí)對(duì)象實(shí)現(xiàn)了相同接口的中間層,這個(gè)中間層保留了對(duì)真實(shí)對(duì)象的引用并向他們發(fā)送請(qǐng)求**。然而他們的設(shè)計(jì)目的是不同的:
Decorator側(cè)重動(dòng)態(tài)為實(shí)體增加功能,因此在該模式中:
- 實(shí)體只實(shí)現(xiàn)了部分功能,Decorator實(shí)現(xiàn)了其他的增強(qiáng)功能;
- 支持遞歸組合(增加多重功能);
- Decorator不知道自己裝飾的是哪個(gè)具體對(duì)象,client必須自己手動(dòng)將實(shí)體和Decorator關(guān)聯(lián)起來(lái)。
Proxy的目的則是當(dāng)訪(fǎng)問(wèn)一個(gè)特定實(shí)體不方便或不符合要求時(shí),為這個(gè)實(shí)體提供一個(gè)替代者,因此:
- 實(shí)體實(shí)現(xiàn)了關(guān)鍵功能,Proxy提供(或拒絕)對(duì)它的訪(fǎng)問(wèn);
- 不支持遞歸組合;
- Proxy向client屏蔽RealSubject的存在,client只能拿到Proxy;
- Proxy確定地知道自己的代理目標(biāo)是RealSubject,因此它和RealSubject相關(guān)聯(lián)而不是Subject接口;此外,它們的關(guān)系是靜態(tài)的,無(wú)法在運(yùn)行時(shí)改變Proxy代理的目標(biāo)對(duì)象。
3. Bridge 橋接模式
作用
你有一個(gè)產(chǎn)品,它在兩個(gè)維度上都是可變化的,如果用繼承,則需要n*m個(gè)子類(lèi)。Bridge模式將兩個(gè)維度的繼承體系獨(dú)立出來(lái),并在二者之間用組合進(jìn)行裝配,避免類(lèi)的泛濫。
進(jìn)一步地考慮,一個(gè)產(chǎn)品的繼承體系應(yīng)該只有一個(gè)維度,如果出現(xiàn)了其他維度上的繼承,要考慮該維度是否是行為/實(shí)現(xiàn)相關(guān)的。對(duì)于行為/實(shí)現(xiàn)方面的變化,應(yīng)當(dāng)先把行為獨(dú)立地抽象出來(lái),并與原產(chǎn)品組合(這就是策略模式的含義),而不應(yīng)該直接在原產(chǎn)品上通過(guò)繼承表達(dá)該行為的變化。
舉個(gè)例子,假如系統(tǒng)內(nèi)要發(fā)送消息,消息按迫切程度分為普通/加急/特急,消息的發(fā)送形式也可以多樣,比如站內(nèi)信/短信/email,每種消息都要求可以用任意方式發(fā)送:

如果簡(jiǎn)單地用繼承,則需要33 = 9個(gè)類(lèi)。但實(shí)際上,消息的發(fā)送* 這個(gè)維度屬于行為,不要用繼承來(lái)表達(dá)行為的變化,這樣會(huì)污染原本的抽象層次,應(yīng)當(dāng)用策略模式將 消息發(fā)送 這個(gè)行為分離。
采用Bridge模式:

實(shí)現(xiàn)

產(chǎn)品的抽象 + 行為的分離(策略模式)
總結(jié)
Bridge模式在我看來(lái)是對(duì)策略模式的擴(kuò)展,它的核心有兩點(diǎn):
- 只在一個(gè)維度上用繼承,出現(xiàn)了多個(gè)維度則考慮分離并用組合,避免類(lèi)的泛濫和抽象維度的混雜;
- 用
策略模式隔離行為的變化,不要讓行為/實(shí)現(xiàn)的變化污染原本的繼承體系。
4. Decorator 裝飾者
作用
有一系列產(chǎn)品,你希望動(dòng)態(tài)地為他們添加額外 / 可自由組合 的功能,并且不影響產(chǎn)品本身。
Attach additional responsibilities to an object dynamically.
實(shí)現(xiàn)

-
Decorator 裝飾器繼承產(chǎn)品抽象接口,并在內(nèi)部持有一個(gè)產(chǎn)品(可能是具體產(chǎn)品,也有可能被裝飾過(guò)了); -
Decorator的具體實(shí)現(xiàn),為其裝飾的產(chǎn)品提供額外的功能,類(lèi)似遞歸的調(diào)用; - 可以同時(shí)反復(fù)應(yīng)用多個(gè)
Decorator,實(shí)現(xiàn)額外功能的動(dòng)態(tài)組合。
應(yīng)用
-
java的IO流的設(shè)計(jì)是一個(gè)典型的裝飾者模式:
IO Stream設(shè)計(jì)
ByteArrayInputStream | FileInputStream | ObjectInputStream | StringBufferInputStream是具體的輸入流產(chǎn)品,根據(jù)數(shù)據(jù)來(lái)源區(qū)分;
FilterInputStream是裝飾器;
BufferedInputStream | DataInputStream | LineNumberInputStream | PushbackInputStream是具體的裝飾器,分別為其他輸入流提供緩沖/類(lèi)型讀寫(xiě)/跟蹤行號(hào)/退回已讀數(shù)據(jù)的功能,這些裝飾器是可以組合使用的:
InputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream("test.txt")));
-
Decorator模式也可以用來(lái)實(shí)現(xiàn)AOP的類(lèi)似功能(雖然實(shí)際大部分都是用JDK動(dòng)態(tài)代理 / 運(yùn)行時(shí)修改字節(jié)碼),Decorator的具體實(shí)現(xiàn)就是我們想要獨(dú)立出來(lái)的切面,產(chǎn)品的具體實(shí)現(xiàn)則是我們想要保持獨(dú)立的業(yè)務(wù)邏輯。
5. Composite 組合模式
作用
實(shí)現(xiàn)樹(shù)形結(jié)構(gòu),并讓用戶(hù)可以用統(tǒng)一的接口對(duì)待葉子節(jié)點(diǎn)和非葉子節(jié)點(diǎn)。
實(shí)現(xiàn)

- 操作孩子的方法應(yīng)該放在Component中嗎?畢竟Leaf是不支持這些操作的。
出于透明性考慮,應(yīng)該放在Component中,Leaf對(duì)這些方法就提供一個(gè)空的實(shí)現(xiàn)。 - Component除了保存孩子,也可以記錄父親;
應(yīng)用
UI / 人員組織管理這種典型的樹(shù)形結(jié)構(gòu)中用的比較廣泛。
6. Facade 門(mén)面模式
作用
一個(gè)系統(tǒng)對(duì)外提供服務(wù),系統(tǒng)暴露的接口應(yīng)該是簡(jiǎn)單而統(tǒng)一的,客戶(hù)不應(yīng)該直接和系統(tǒng)內(nèi)復(fù)雜的子部件進(jìn)行交互,而應(yīng)只依賴(lài)于一個(gè)單一的高層接口,該組件為客戶(hù)屏蔽了內(nèi)部的復(fù)雜性,降低了客戶(hù)和系統(tǒng)的耦合。

更像是一種設(shè)計(jì)思路,而非一個(gè)具體模式。
三. 行為模式
1. Chain of Responsibility 責(zé)任鏈
作用
客戶(hù)端發(fā)出一個(gè)請(qǐng)求,有一系列的處理器都有機(jī)會(huì)處理這個(gè)請(qǐng)求,但具體哪個(gè)是運(yùn)行時(shí)決定的,客戶(hù)端也不知道究竟誰(shuí)會(huì)來(lái)處理。
因此將所有處理器組成一個(gè)鏈條,將請(qǐng)求從鏈條中流過(guò),每個(gè)處理器查看是否應(yīng)該處理它,如果不是,則交給后面的處理器,否則處理并退出。處理器在鏈中的位置決定其優(yōu)先級(jí)。
將請(qǐng)求者和處理者解耦,可以動(dòng)態(tài)切換/組合處理者。
實(shí)現(xiàn)

擴(kuò)展
客戶(hù)端發(fā)出一個(gè)請(qǐng)求,請(qǐng)求的處理分為很多步驟,這些步驟是不確定的/可以動(dòng)態(tài)組合的,甚至需要支持在運(yùn)行時(shí)改變步驟,或者在步驟間任意跳轉(zhuǎn)。
解決方案和責(zé)任鏈類(lèi)似,將處理流程抽象為一個(gè)處理器鏈條,鏈條的組裝交給外部決定。每個(gè)處理器對(duì)請(qǐng)求完成自己負(fù)責(zé)的業(yè)務(wù)邏輯,并看情況結(jié)束/傳遞給下一個(gè)處理器/跳轉(zhuǎn)到任意處理器。
這和標(biāo)準(zhǔn)的責(zé)任鏈的結(jié)構(gòu)基本一樣,但他們的目的不一樣。標(biāo)準(zhǔn)責(zé)任鏈目的是動(dòng)態(tài) 找到請(qǐng)求的處理者 ;擴(kuò)展(某些地方稱(chēng)為“功能鏈”?)則是為了獲取 動(dòng)態(tài)拼裝和改變處理流程 的能力。
應(yīng)用
標(biāo)準(zhǔn)責(zé)任鏈
UI中的事件冒泡機(jī)制是責(zé)任鏈的一個(gè)典型應(yīng)用。HTML中,點(diǎn)擊一個(gè)DOM元素,產(chǎn)生的click事件將依次冒泡給它的父元素,每個(gè)父元素上都可以注冊(cè)對(duì)click事件的監(jiān)聽(tīng)器,監(jiān)聽(tīng)器中除了對(duì)事件處理外,也可以結(jié)束事件的繼續(xù)冒泡。
擴(kuò)展(功能鏈)
- Web應(yīng)用中的各種filter/攔截器;
- Netty中的pipeline
2. Command 命令
Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
你需要向一個(gè)對(duì)象提交請(qǐng)求,但對(duì)請(qǐng)求的處理是動(dòng)態(tài)的,無(wú)法寫(xiě)死。比如一個(gè)菜單項(xiàng),在不同的上下文中,點(diǎn)擊它要做的事情顯然是不一樣的。
Command模式的思路是 抽象請(qǐng)求(及處理):

-
Client裝配Invoker和Command,** 如果需要不同的處理,裝配不同的Command即可 **; -
Client向Invoker發(fā)出請(qǐng)求; -
Invoker將請(qǐng)求的處理委托給Command#execute()。
很多時(shí)候Command不夠智能,自己無(wú)法處理請(qǐng)求,需要將請(qǐng)求委托給另一個(gè)Receiver進(jìn)行真正的處理,ConcreteCommand可以認(rèn)為是Receiver的適配器:

可以看到,Command模式的最大價(jià)值在于:隔離 請(qǐng)求的接收者 和 請(qǐng)求的處理邏輯;
此外,將請(qǐng)求及其處理邏輯抽象為Command后可以做很多有意思的事情:
1. 可撤銷(xiāo)的操作
在Command接口中增加一個(gè)接口 undo() 實(shí)現(xiàn)單個(gè)命令的撤銷(xiāo)動(dòng)作,并用一個(gè)stack保存所有Command;當(dāng)用戶(hù)觸發(fā)撤銷(xiāo)時(shí)依次從stack pop出最近的Command,執(zhí)行其undo()方法。
2. 宏命令
宏命令實(shí)質(zhì)是個(gè)樹(shù)形結(jié)構(gòu),對(duì)Command應(yīng)用Composite 組合模式即可實(shí)現(xiàn):

3. 排隊(duì)
4. 日志記錄和恢復(fù)
3. Memento 備忘錄
有些情況下你需要記錄一個(gè)對(duì)象(稱(chēng)為Originator)在某個(gè)時(shí)刻的狀態(tài)(snapshot),以便后續(xù)恢復(fù),我們可以用一個(gè)類(lèi)Memento表示snapshot,它包含了Originator的部分或全部狀態(tài):

- Originator 負(fù)責(zé)創(chuàng)建Memento,以及恢復(fù)到某個(gè)Memento;
- Memento 即Originator的snapshot;
- Caretaker 充當(dāng)協(xié)調(diào)者,它負(fù)責(zé)向Originator請(qǐng)求當(dāng)前Memento / 保存Memento / 在后續(xù)某個(gè)時(shí)刻讓Originator恢復(fù)到某個(gè)Memento。
但這里有一個(gè)問(wèn)題,為了隱藏Originator的實(shí)現(xiàn)細(xì)節(jié),Memento必須向外部隱藏內(nèi)部數(shù)據(jù),即不開(kāi)放state 的 getter/setter 給外部,但這樣一來(lái),Originator也無(wú)法創(chuàng)建Memento了。
為了解決這個(gè)問(wèn)題,在Memento模式的一般實(shí)現(xiàn)中,Memento 類(lèi)被分為兩個(gè)部分:

- 標(biāo)記接口
Memento,空的,Caretaker只能得到這個(gè)接口; - Memento的真正實(shí)現(xiàn)
MementoImpl,作為Originator的 私有內(nèi)部類(lèi) ,這樣既允許Originator訪(fǎng)問(wèn)Memento的內(nèi)部狀態(tài),又滿(mǎn)足了Memento向外部(主要是Caretaker)隱藏內(nèi)部細(xì)節(jié)的要求。
如果對(duì) Memento 的封裝性沒(méi)有嚴(yán)格的要求,第一種實(shí)現(xiàn)顯然更簡(jiǎn)單。
4. Observer 觀(guān)察者
作用
定義一個(gè)一對(duì)多關(guān)系,在Subject狀態(tài)發(fā)生改變時(shí),所有Observer獲得通知。
解耦 事件發(fā)生者 & 事件接收者,使得雙方的改動(dòng)互不影響,關(guān)聯(lián)關(guān)系也可動(dòng)態(tài)改變。
實(shí)現(xiàn)

數(shù)據(jù)傳遞的兩種方式:
- 推:由 Subject 主動(dòng)向 Observer 推送信息,而不管信息對(duì)后者而言是否需要/是否足夠。
- 拉:Subject 把自己傳遞給 Observer,由 Observer 從 Subject 拉取自己需要的信息。
擴(kuò)展: Observer 注冊(cè)時(shí)可以指定自己感興趣的事件。
擴(kuò)展:EventBus
傳統(tǒng)的Observer模式中,事件發(fā)生者和接收者依然存在耦合,發(fā)生者需要管理接收者的集合,我們可以進(jìn)一步地,在Subject和Observer間增加一個(gè)中間層負(fù)責(zé)轉(zhuǎn)發(fā)事件,將它們徹底地解耦;進(jìn)一步,這個(gè)事件轉(zhuǎn)發(fā)者可以是通用的,支持任意發(fā)布者和接受者,通常稱(chēng)之為EventBus,是一種廣泛應(yīng)用的架構(gòu)。
5. State 狀態(tài)模式
作用
模擬狀態(tài)機(jī),描述一個(gè)對(duì)象(Context)的狀態(tài)變遷,將特性狀態(tài)下的行為分割開(kāi)來(lái),避免在Context中用大量的if維護(hù)所有狀態(tài)的變遷,而且容易擴(kuò)展新的狀態(tài)。
實(shí)現(xiàn)

-
Context中記錄它自己當(dāng)前的狀態(tài); -
Context接收一個(gè)輸入動(dòng)作,并將該輸入委托給當(dāng)前所處State處理; -
State處理輸入,根據(jù)需要讓Context躍遷到另一狀態(tài)。
如果State不保存狀態(tài)則可以是單例的,Java中,可以用enum類(lèi)型實(shí)現(xiàn)State。
6. Strategy 策略模式
作用
你有一個(gè)對(duì)象負(fù)責(zé)完成某件事情,但在不同時(shí)刻其使用的算法是不同的,Strategy模式將可變的算法獨(dú)立并封裝,避免大量if條件判斷,并方便替換和擴(kuò)展。
Strategy 封裝了 相同行為的不同實(shí)現(xiàn)
實(shí)現(xiàn)

Strategy的實(shí)現(xiàn)通常依賴(lài)Context的數(shù)據(jù),后者在調(diào)用前者的方法時(shí)需要將自己傳遞過(guò)去。
實(shí)際應(yīng)用中,經(jīng)常會(huì)發(fā)現(xiàn)不同的策略其算法骨架類(lèi)似,只有某些具體步驟不同,此時(shí)可以對(duì)Strategy應(yīng)用Template Method模式。
7. Template Method 模板方法
作用
將一個(gè)算法的通用骨架抽象到父類(lèi)以避免代碼重復(fù),而將一些可變的步驟延遲到子類(lèi),子類(lèi)不用關(guān)心算法結(jié)構(gòu),只需關(guān)注自己需要實(shí)現(xiàn)的特定步驟。
實(shí)現(xiàn)

這個(gè)沒(méi)什么好說(shuō)的。
四. 設(shè)計(jì)原則
-
SRP原則 - 單一職責(zé)原則(Single Responsibility Principle)
每個(gè)類(lèi)實(shí)現(xiàn)單一職責(zé),并且單一職責(zé)都有清楚明確的定義,有利于降低軟件的復(fù)雜性, 提高可讀性、可維護(hù)性和可擴(kuò)展性。
軟件設(shè)計(jì)應(yīng)遵守單一職責(zé)原則,將不同的職責(zé)封裝到不同的類(lèi)或模塊中。
-
OCP法則 - 開(kāi)放-關(guān)閉原則
一個(gè)類(lèi)應(yīng)當(dāng)對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。即當(dāng)有新的需求時(shí),不是修改已有的類(lèi),而是對(duì)已有的類(lèi)進(jìn)行擴(kuò)展。
- 通過(guò)擴(kuò)展已有軟件系統(tǒng),可以提供新的行為,以滿(mǎn)足對(duì)軟件的新的需求,使變化中的軟件有一定的適應(yīng)性和靈活性。
- 已有軟件模塊,特別是最重要的抽象層模塊不能再修改,這使變化中的軟件系統(tǒng)有一定的穩(wěn)定性和延續(xù)性。
實(shí)現(xiàn)開(kāi)閉原則的關(guān)鍵在于 分離不變和變化的部分,并對(duì)變化的部分進(jìn)行合理的高層抽象,并讓不變的部分依賴(lài)該高層抽象,這樣就能在不同的實(shí)現(xiàn)間切換,或者擴(kuò)展新的實(shí)現(xiàn)。很多設(shè)計(jì)模式都體現(xiàn)了這一點(diǎn),比如策略模式將算法抽象出來(lái),模板方法將不變的算法骨架與易變的需要自定義的步驟隔離,裝飾者模式將不變的核心功能對(duì)象和易變的增強(qiáng)功能隔離等等。
LSP法則 - 里氏替換原則(Liskov Substitution Principle)
把抽象接口和實(shí)現(xiàn)分離,子類(lèi)必須能替換掉父類(lèi),這個(gè)原則通常由語(yǔ)言保證。DIP法則 - 依賴(lài)倒置原則(Dependence Inversion Principle)
抽象不應(yīng)當(dāng)依賴(lài)于細(xì)節(jié);細(xì)節(jié)應(yīng)當(dāng)依賴(lài)于抽象;要針對(duì)接口編程,不針對(duì)實(shí)現(xiàn)編程。具體講就是要依賴(lài)于抽象,不要依賴(lài)于具體。
高層不直接依賴(lài)底層,而是高層定義自己需要底層提供什么樣的接口,底層負(fù)責(zé)實(shí)現(xiàn),這樣就可以隨意切換底層的具體實(shí)現(xiàn)而不用影響高層,但底層反而要依賴(lài)高層公布的接口,所以稱(chēng)為“依賴(lài)倒置”。ISP原則 - 接口分離原則 (Interface Segregation Principle)
每一個(gè)接口應(yīng)該是一種角色,不多不少,不干不該干的事,該干的事都要干。這類(lèi)似編碼原則中的最小權(quán)限法則。軟件設(shè)計(jì)不應(yīng)出現(xiàn)龐大的接口,迫使客戶(hù)在使用時(shí)必須從一大堆它不需要的方法中尋找目的方法。這樣的接口應(yīng)該按照不同客戶(hù)的需求被分離成若干小接口。-
LKP原則 - 最少知識(shí)原則(Least Knowledge Principle)
也稱(chēng)為迪米特法則。原則的核心思想即一個(gè)對(duì)象應(yīng)對(duì)其他對(duì)象有盡可能少的了解。
類(lèi)應(yīng)當(dāng)只與自己的朋友交互。該原則的思想是,將類(lèi)對(duì)外部的了解盡量保持在一定范圍內(nèi),盡量減少類(lèi)之間的交互,從而降低各個(gè)組件間的耦合。
“朋友”的定義:
- 當(dāng)前對(duì)象的屬性
- 當(dāng)前對(duì)象所創(chuàng)建的對(duì)象
- 方法參數(shù)傳遞進(jìn)來(lái)的參數(shù)
- 方法內(nèi)創(chuàng)建的對(duì)象
五. 參考與推薦
- 《大話(huà)設(shè)計(jì)模式》
- 《深入淺出設(shè)計(jì)模式》
- 《Java與模式》
- 《敏捷軟件開(kāi)發(fā)原則、模式與實(shí)踐》
- 《設(shè)計(jì)模式--可復(fù)用面向?qū)ο筌浖幕A(chǔ)》
- 《設(shè)計(jì)模式精解》
