定義
Define a family of algorithms,encapsulate each one,and make them interchangeable.
定義一組算法,將每個(gè)算法都封裝起來歼狼,并且使它們之間可以互換绪穆。
策略模式使用的就是面向?qū)ο蟮睦^承和多態(tài)機(jī)制蘸际,非常容易理解和掌握
實(shí)現(xiàn)
抽象策略
策略苦始、算法家族的抽象擂达,通常為接口蹋肮,也可以是抽象類待侵,定義每個(gè)策略或算法必須具有的方法和屬性丢早。
public interface Strategy {
/**
* 策略模式的運(yùn)算法則
*/
void doSomething();
}
具體策略
實(shí)現(xiàn)抽象策略中的操作,該類含有具體的算法
public class ConcreteStrategyA implements Strategy {
@Override
public void doSomething() {
System.out.println("具體策略A的運(yùn)算法則");
}
}
public class ConcreteStrategyB implements Strategy {
@Override
public void doSomething() {
System.out.println("具體策略B的運(yùn)算法則");
}
}
封裝類
也叫做上下文類或環(huán)境類秧倾,起承上啟下封裝作用怨酝,屏蔽高層模塊對(duì)策略、算法的直接訪問那先,封裝可能存在的變化农猬。
策略模式的重點(diǎn)就是封裝角色,它是借用了代理模式的思路售淡,和代理模式的差別就是策略模式的封裝角色和被封裝的策略類不用是同一個(gè)接口斤葱,如果是同一個(gè)接口那就成為了代理模式。
public class Context {
/**
* 抽象策略
*/
private Strategy strategy;
/**
* 構(gòu)造函數(shù)設(shè)置具體策略
*
* @param strategy
*/
public Context(Strategy strategy) {
this.strategy = strategy;
}
/**
* 封裝后的策略方法
*/
public void executeStrategy() {
this.strategy.doSomething();
}
}
客戶端代碼
public class Client {
public static void main(String[] args) {
// 聲明一個(gè)具體的策略
Strategy strategyA = new ConcreteStrategyA();
// 聲明上下文對(duì)象
Context contextA = new Context(strategyA);
// 執(zhí)行封裝后的方法
contextA.executeStrategy();
Strategy strategyB = new ConcreteStrategyB();
Context contextB = new Context(strategyB);
contextB.executeStrategy();
}
}
優(yōu)點(diǎn)
-
算法可以自由切換
這是策略模式本身定義的揖闸,只要實(shí)現(xiàn)抽象策略揍堕,它就成為策略家族的一個(gè)成員,通過封裝角色對(duì)其進(jìn)行封裝汤纸,保證對(duì)外提供“可自由切換”的策略衩茸。
-
避免使用多重條件判斷
如果沒有策略模式,一個(gè)策略家族有5個(gè)策略算法贮泞,一會(huì)要使用A策略楞慈,一會(huì)要使用B策略,怎么設(shè)計(jì)呢啃擦?使用多重的條件語句抖部?多重條件語句不易維護(hù),而且出錯(cuò)的概率大大增強(qiáng)议惰。使用策略模式后,可以由其他模塊決定采用何種策略乡恕,策略家族對(duì)外提供的訪問接口就是封裝類言询,簡(jiǎn)化了操作俯萎,同時(shí)避免了條件語句判斷。
-
擴(kuò)展性良好
在現(xiàn)有的系統(tǒng)中增加一個(gè)策略太容易了运杭,只要實(shí)現(xiàn)接口就可以了夫啊,其他都不用修改,類似于一個(gè)可反復(fù)拆卸的插件辆憔,這大大地符合了OCP原則撇眯。
缺點(diǎn)
-
策略類數(shù)量增多
每一個(gè)策略都是一個(gè)類,復(fù)用的可能性很小虱咧,類數(shù)量增多熊榛。
-
所有的策略類都需要對(duì)外暴露
上層模塊必須知道有哪些策略,然后才能決定使用哪一個(gè)策略腕巡,這與迪米特法則是相違背的(只是想使用一個(gè)策略玄坦,卻要要了解這個(gè)策略)』娉粒可以使用其他模式來修正這個(gè)缺陷煎楣,如工廠方法模式、代理模式或享元模式车伞。
使用場(chǎng)景
多個(gè)類只有在算法或行為上稍有不同的場(chǎng)景择懂。
-
算法需要自由切換的場(chǎng)景。
例如另玖,算法的選擇是由使用者決定的困曙,或者算法始終在進(jìn)化,特別是一些站在技術(shù)前沿的行業(yè)日矫,連業(yè)務(wù)專家都無法給你保證這樣的系統(tǒng)規(guī)則能夠存在多長(zhǎng)時(shí)間赂弓,在這種情況下策略模式是你最好的助手。
-
需要屏蔽算法規(guī)則的場(chǎng)景哪轿。
現(xiàn)在的科技發(fā)展得很快盈魁,人腦的記憶是有限的(就目前來說是有限的),太多的算法你只要知道一個(gè)名字就可以了窃诉,傳遞相關(guān)的數(shù)字進(jìn)來杨耙,反饋一個(gè)運(yùn)算結(jié)果,萬事大吉飘痛。
注意事項(xiàng)
如果系統(tǒng)中的一個(gè)策略家族的具體策略數(shù)量超過4個(gè)珊膜,則需要考慮使用混合模式,解決策略類膨脹和對(duì)外暴露的問題宣脉,否則日后的系統(tǒng)維護(hù)就會(huì)成為一個(gè)燙手山芋车柠,誰都不想接。
最佳實(shí)踐
策略模式是一個(gè)非常簡(jiǎn)單的模式(主要是用了Java繼承與多態(tài)的機(jī)制)。它在項(xiàng)目中使用得非常多竹祷,但單獨(dú)使用的地方就比較少了谈跛,因?yàn)樗兄旅毕荩?strong>所有的策略都需要暴露出去,這樣才方便客戶端決定使用哪一個(gè)策略塑陵。我們的策略模式只是實(shí)現(xiàn)了策略的管理感憾,但是沒有嚴(yán)格地定義“適當(dāng)?shù)膱?chǎng)景”使用“適當(dāng)?shù)牟呗浴保趯?shí)際項(xiàng)目中令花,一般通過工廠方法模式來實(shí)現(xiàn)策略類的聲明阻桅。
擴(kuò)展(策略枚舉)
定義
- 它是一個(gè)枚舉。
- 它是一個(gè)濃縮了的策略模式的枚舉兼都。
啥意思嫂沉?來看代碼:
定義一個(gè)計(jì)算器(枚舉類)
public enum Calculator {
PLUS("+") {
public int exec(int x, int y) {
return x + y;
}
},
MINUS("-") {
public int exec(int x, int y) {
return x - y;
}
};
private final String symbol;
Calculator(String symbol) {
this.symbol = symbol;
}
public String getSymbol() {
return this.symbol;
}
/**
* 聲明一個(gè)抽象方法
* 枚舉類型中的抽象方法必須被它的所有常量中的具體方法所覆蓋(被稱為特定于常量的方法實(shí)現(xiàn))
*/
public abstract int exec(int a, int b);
}
把原有定義在抽象策略中的方法移植到枚舉中,每個(gè)枚舉成員就成為一個(gè)具體策略
客戶端代碼
public class Client {
public static void main(String[] args) {
int x = 100;
int y = 10;
System.out.println(x + " + " + y + " = " + Calculator.PLUS.exec(x, y));
System.out.println(x + " - " + y + " = " + Calculator.MINUS.exec(x, y));
}
}
代碼量非常少俯抖,而且還有一個(gè)顯著的優(yōu)點(diǎn):真實(shí)地面向?qū)ο?/p>
Calculator.PLUS.exec(x, y)類似于“拿出計(jì)算器(Calculator)输瓜,對(duì)x和y進(jìn)行加法運(yùn)算(MINUS),并立刻執(zhí)行(exec)”芬萍,這與我們?nèi)粘=佑|邏輯非常相似
策略枚舉是一個(gè)非常優(yōu)秀和方便的模式(《Effective Java》中枚舉相關(guān)條目也有詳細(xì)介紹該模式)尤揣,但是它受枚舉類型的限制,每個(gè)枚舉項(xiàng)都是public柬祠、final北戏、static的,擴(kuò)展性受到了一定的約束漫蛔,因此在系統(tǒng)開發(fā)中嗜愈,策略枚舉一般擔(dān)當(dāng)不經(jīng)常發(fā)生變化的角色。
源碼地址:https://gitee.com/tianranll/java-design-patterns.git
參考文獻(xiàn):《設(shè)計(jì)模式之禪》莽龟、《Effective Java》