相信大家都用過計(jì)算器,輸入一個(gè)數(shù),然后輸入運(yùn)算符,然后再輸入一個(gè)數(shù),就會(huì)根據(jù)不同的運(yùn)算符做不同的運(yùn)算。
最直接的加減法:
public class Calculator {
//加符號(hào)
private final static String ADD_SYMBOL = "+";
//減符號(hào)
private final static String SUB_SYMBOL = "-";
public int exec(int a,int b,String symbol){
int result =0;
if(symbol.equals(ADD_SYMBOL)){
result = this.add(a, b);
}else if(symbol.equals(SUB_SYMBOL)){
result = this.sub(a, b);
}
return result;
}
//加法運(yùn)算
private int add(int a,int b){
return a+b;
}
//減法運(yùn)算
private int sub(int a,int b){
return a-b;
}
}
用戶使用:
public class Client {
public static void main(String[] args) {
//輸入的兩個(gè)參數(shù)是數(shù)字
int a = Integer.parseInt(args[0]);
String symbol = args[1]; //符號(hào)
int b = Integer.parseInt(args[2]);
System.out.println("輸入的參數(shù)為:"+Arrays.toString(args));
//生成一個(gè)運(yùn)算器
Calculator cal = new Calculator();
System.out.println("運(yùn)行結(jié)果為:"+a + symbol + b + "=" + cal.exec(a, b, symbol));
}
}
這是最簡(jiǎn)單直接的代碼,有什么問題嗎?假如用戶需要這個(gè)計(jì)算器支持乘法呢?就要改Calculator類,明顯違背了開閉原則,系統(tǒng)也不利于維護(hù)。
那么怎么設(shè)計(jì)成可以擴(kuò)展的代碼呢?就需要策略模式了。

定義:策略模式也叫政策模式,定義一組算法,將每個(gè)算法都封裝起來,并且使它們之間可以互換。
這個(gè)定義是非常明確、清晰的,“定義一組算法”,看看加減法和乘法是不是三個(gè)算
法?“將每個(gè)算法都封裝起來”,那么我們定義一個(gè)類,封裝算法,可以互換,是不是多態(tài)的特征呢?我們用代碼把這個(gè)定義實(shí)現(xiàn)下:
//抽象策略
interface Calculator {
public int exec(int a,int b);
}
// 具體策略
public class Add implements Calculator {
// 加法運(yùn)算
public int exec(int a, int b) {
return a+b;
}
}
public class Sub implements Calculator {
//減法運(yùn)算
public int exec(int a, int b) {
return a-b;
}
}
策略定義好了,然后定義一個(gè)Context封裝類,其作用是承裝三個(gè)策略,根據(jù)不同的需要替換:
public class Context {
private Calculator cal = null;
public Context(Calculator _cal){
this.cal = _cal;
}
public int exec(int a,int b,String symbol){
return this.cal.exec(a, b);
}
}
用戶使用:
public class Client {
//加符號(hào)
public final static String ADD_SYMBOL = "+";
//減符號(hào)
public final static String SUB_SYMBOL = "-";
public static void main(String[] args) {
//輸入的兩個(gè)參數(shù)是數(shù)字
int a = Integer.parseInt(args[0]);
String symbol = args[1]; //符號(hào)
int b = Integer.parseInt(args[2]);
System.out.println("輸入的參數(shù)為:"+Arrays.toString(args));
//上下文
Context context = null;
//判斷初始化哪一個(gè)策略
if(symbol.equals(ADD_SYMBOL)){
context = new Context(new Add());
}else if(symbol.equals(SUB_SYMBOL)){
context = new Context(new Sub());
}
System.out.println("運(yùn)行結(jié)果為:"+a+symbol+b+"="+context.exec(a,b,symbol));
}
}
需要增加乘法呢?實(shí)現(xiàn)Calculator ,增加乘法算法,直接替換就ok了:
public class Mul implements Calculator {
//乘法運(yùn)算
public int exec(int a, int b) {
return a*b;
}
}
public class Client {
//加符號(hào)
public final static String ADD_SYMBOL = "+";
//減符號(hào)
public final static String SUB_SYMBOL = "-";
//乘符號(hào)
public final static String MUL_SYMBOL = "*";
public static void main(String[] args) {
//輸入的兩個(gè)參數(shù)是數(shù)字
int a = Integer.parseInt(args[0]);
String symbol = args[1]; //符號(hào)
int b = Integer.parseInt(args[2]);
System.out.println("輸入的參數(shù)為:"+Arrays.toString(args));
//上下文
Context context = null;
//判斷初始化哪一個(gè)策略
if(symbol.equals(ADD_SYMBOL)){
context = new Context(new Add());
}else if(symbol.equals(SUB_SYMBOL)){
context = new Context(new Sub());
}else if(symbol.equals(MUL_SYMBO)){
context = new Context(new Mul());
}
System.out.println("運(yùn)行結(jié)果為:"+a+symbol+b+"="+context.exec(a,b,symbol));
}
}
我們總結(jié)下這樣的寫的優(yōu)點(diǎn):
- 算法可以自由切換
這是策略模式本身定義的,只要實(shí)現(xiàn)抽象策略,它就成為策略家族的一個(gè)成員,通過封
裝角色對(duì)其進(jìn)行封裝,保證對(duì)外提供“可自由切換”的策略。 - 避免使用多重條件判斷
如果沒有策略模式,我們想想看會(huì)是什么樣子?一個(gè)策略家族有5個(gè)策略算法,一會(huì)要
使用A策略,一會(huì)要使用B策略,怎么設(shè)計(jì)呢?使用多重的條件語句?多重條件語句不易維
護(hù),而且出錯(cuò)的概率大大增強(qiáng)。使用策略模式后,可以由其他模塊決定采用何種策略,策略
家族對(duì)外提供的訪問接口就是封裝類,簡(jiǎn)化了操作,同時(shí)避免了條件語句判斷。 - 擴(kuò)展性良好
這甚至都不用說是它的優(yōu)點(diǎn),因?yàn)樗黠@了。在現(xiàn)有的系統(tǒng)中增加一個(gè)策略太容易
了,只要實(shí)現(xiàn)接口就可以了,其他都不用修改,類似于一個(gè)可反復(fù)拆卸的插件,這大大地符合了OCP原則。
當(dāng)然他也不可避免的具有缺點(diǎn):
- 策略類數(shù)量增多
每一個(gè)策略都是一個(gè)類,復(fù)用的可能性很小,類數(shù)量增多。 - 所有的策略類都需要對(duì)外暴露
上層模塊必須知道有哪些策略,然后才能決定使用哪一個(gè)策略,這與迪米特法則是相違
背的,我只是想使用了一個(gè)策略,我憑什么就要了解這個(gè)策略呢?那要你的封裝類還有什么
意義?這是原裝策略模式的一個(gè)缺點(diǎn),幸運(yùn)的是,我們可以使用其他模式來修正這個(gè)缺陷,
如工廠方法模式、代理模式或享元模式。
那我們什么應(yī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é)果,萬事大吉。
Android中有一個(gè)需求場(chǎng)景是不是特別像?有數(shù)據(jù)的時(shí)候,要展示數(shù)據(jù);無網(wǎng)絡(luò)的時(shí)候,展示重試界面。對(duì)的,就是狀態(tài)策略,根據(jù)不同的狀態(tài)選取不同的策略,但是我們一般不單獨(dú)使用策略模式,而是使用工廠方法來實(shí)現(xiàn)策略類的聲明。也就是利用混編,揚(yáng)長(zhǎng)避短,達(dá)到最優(yōu)的設(shè)計(jì)。