相信大家都用過計算器玷过,輸入一個數(shù)苛蒲,然后輸入運算符卤橄,然后再輸入一個數(shù),就會根據(jù)不同的運算符做不同的運算臂外。
最直接的加減法:
public class Calculator {
//加符號
private final static String ADD_SYMBOL = "+";
//減符號
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;
}
//加法運算
private int add(int a,int b){
return a+b;
}
//減法運算
private int sub(int a,int b){
return a-b;
}
}
用戶使用:
public class Client {
public static void main(String[] args) {
//輸入的兩個參數(shù)是數(shù)字
int a = Integer.parseInt(args[0]);
String symbol = args[1]; //符號
int b = Integer.parseInt(args[2]);
System.out.println("輸入的參數(shù)為:"+Arrays.toString(args));
//生成一個運算器
Calculator cal = new Calculator();
System.out.println("運行結(jié)果為:"+a + symbol + b + "=" + cal.exec(a, b, symbol));
}
}
這是最簡單直接的代碼窟扑,有什么問題嗎?假如用戶需要這個計算器支持乘法呢漏健?就要改Calculator類嚎货,明顯違背了開閉原則,系統(tǒng)也不利于維護蔫浆。
那么怎么設(shè)計成可以擴展的代碼呢殖属?就需要策略模式了。
定義:策略模式也叫政策模式瓦盛,定義一組算法洗显,將每個算法都封裝起來,并且使它們之間可以互換原环。
這個定義是非常明確挠唆、清晰的,“定義一組算法”扮念,看看加減法和乘法是不是三個算
法损搬?“將每個算法都封裝起來”,那么我們定義一個類柜与,封裝算法巧勤,可以互換,是不是多態(tài)的特征呢弄匕?我們用代碼把這個定義實現(xiàn)下:
//抽象策略
interface Calculator {
public int exec(int a,int b);
}
// 具體策略
public class Add implements Calculator {
// 加法運算
public int exec(int a, int b) {
return a+b;
}
}
public class Sub implements Calculator {
//減法運算
public int exec(int a, int b) {
return a-b;
}
}
策略定義好了颅悉,然后定義一個Context封裝類,其作用是承裝三個策略迁匠,根據(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 {
//加符號
public final static String ADD_SYMBOL = "+";
//減符號
public final static String SUB_SYMBOL = "-";
public static void main(String[] args) {
//輸入的兩個參數(shù)是數(shù)字
int a = Integer.parseInt(args[0]);
String symbol = args[1]; //符號
int b = Integer.parseInt(args[2]);
System.out.println("輸入的參數(shù)為:"+Arrays.toString(args));
//上下文
Context context = null;
//判斷初始化哪一個策略
if(symbol.equals(ADD_SYMBOL)){
context = new Context(new Add());
}else if(symbol.equals(SUB_SYMBOL)){
context = new Context(new Sub());
}
System.out.println("運行結(jié)果為:"+a+symbol+b+"="+context.exec(a,b,symbol));
}
}
需要增加乘法呢剩瓶?實現(xiàn)Calculator ,增加乘法算法城丧,直接替換就ok了:
public class Mul implements Calculator {
//乘法運算
public int exec(int a, int b) {
return a*b;
}
}
public class Client {
//加符號
public final static String ADD_SYMBOL = "+";
//減符號
public final static String SUB_SYMBOL = "-";
//乘符號
public final static String MUL_SYMBOL = "*";
public static void main(String[] args) {
//輸入的兩個參數(shù)是數(shù)字
int a = Integer.parseInt(args[0]);
String symbol = args[1]; //符號
int b = Integer.parseInt(args[2]);
System.out.println("輸入的參數(shù)為:"+Arrays.toString(args));
//上下文
Context context = null;
//判斷初始化哪一個策略
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("運行結(jié)果為:"+a+symbol+b+"="+context.exec(a,b,symbol));
}
}
我們總結(jié)下這樣的寫的優(yōu)點:
- 算法可以自由切換
這是策略模式本身定義的延曙,只要實現(xiàn)抽象策略,它就成為策略家族的一個成員亡哄,通過封
裝角色對其進行封裝枝缔,保證對外提供“可自由切換”的策略。 - 避免使用多重條件判斷
如果沒有策略模式,我們想想看會是什么樣子愿卸?一個策略家族有5個策略算法灵临,一會要
使用A策略,一會要使用B策略趴荸,怎么設(shè)計呢儒溉?使用多重的條件語句?多重條件語句不易維
護发钝,而且出錯的概率大大增強顿涣。使用策略模式后,可以由其他模塊決定采用何種策略酝豪,策略
家族對外提供的訪問接口就是封裝類园骆,簡化了操作,同時避免了條件語句判斷寓调。 - 擴展性良好
這甚至都不用說是它的優(yōu)點,因為它太明顯了锄码。在現(xiàn)有的系統(tǒng)中增加一個策略太容易
了夺英,只要實現(xiàn)接口就可以了,其他都不用修改滋捶,類似于一個可反復(fù)拆卸的插件痛悯,這大大地符合了OCP原則。
當(dāng)然他也不可避免的具有缺點:
- 策略類數(shù)量增多
每一個策略都是一個類重窟,復(fù)用的可能性很小载萌,類數(shù)量增多。 - 所有的策略類都需要對外暴露
上層模塊必須知道有哪些策略巡扇,然后才能決定使用哪一個策略扭仁,這與迪米特法則是相違
背的,我只是想使用了一個策略厅翔,我憑什么就要了解這個策略呢乖坠?那要你的封裝類還有什么
意義?這是原裝策略模式的一個缺點刀闷,幸運的是熊泵,我們可以使用其他模式來修正這個缺陷,
如工廠方法模式甸昏、代理模式或享元模式顽分。
那我們什么應(yīng)該使用策略模式呢:
- 多個類只有在算法或行為上稍有不同的場景。
- 算法需要自由切換的場景施蜜。
例如卒蘸,算法的選擇是由使用者決定的,或者算法始終在進化花墩,特別是一些站在技術(shù)前沿
的行業(yè)悬秉,連業(yè)務(wù)專家都無法給你保證這樣的系統(tǒng)規(guī)則能夠存在多長時間澄步,在這種情況下策略
模式是你最好的助手。 - 需要屏蔽算法規(guī)則的場景和泌。
現(xiàn)在的科技發(fā)展得很快村缸,人腦的記憶是有限的(就目前來說是有限的),太多的算法你
只要知道一個名字就可以了武氓,傳遞相關(guān)的數(shù)字進來梯皿,反饋一個運算結(jié)果,萬事大吉县恕。
Android中有一個需求場景是不是特別像东羹?有數(shù)據(jù)的時候,要展示數(shù)據(jù)忠烛;無網(wǎng)絡(luò)的時候属提,展示重試界面。對的美尸,就是狀態(tài)策略冤议,根據(jù)不同的狀態(tài)選取不同的策略,但是我們一般不單獨使用策略模式师坎,而是使用工廠方法來實現(xiàn)策略類的聲明恕酸。也就是利用混編,揚長避短胯陋,達(dá)到最優(yōu)的設(shè)計蕊温。