簡(jiǎn)介
Define a family of algorithms,encapsulate each one,and make them interchangeable.
定義一組算法蹬挺,將每個(gè)算法都封裝起來(lái)唬滑,并且使它們之間可以互換域携。
策略模式(Strategy Pattern) 也叫 政策模式(Policy Pattern)鱼喉。指的是對(duì)象具備某個(gè)行為,但是在不同的場(chǎng)景中扛禽,該行為有不同的實(shí)現(xiàn)算法锋边。比如一個(gè)人的交稅比率與他的工資有關(guān),不同的工資水平對(duì)應(yīng)不同的稅率编曼。
策略模式 使用的就是面向?qū)ο蟮睦^承和多態(tài)機(jī)制豆巨,從而實(shí)現(xiàn)同一行為在不同場(chǎng)景下具備不同實(shí)現(xiàn)。
策略模式 本質(zhì):分離算法掐场,選擇實(shí)現(xiàn)
主要解決
在有多種算法相似的情況下往扔,使用 if...else 或 switch...case 所帶來(lái)的復(fù)雜性和臃腫性。
優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
- 算法多樣性熊户,且具備自由切換功能萍膛;
- 有效避免多重條件判斷,增強(qiáng)了封裝性嚷堡,簡(jiǎn)化了操作卦羡,降低出錯(cuò)概率;
- 擴(kuò)展性良好麦到,策略類遵頊 里氏替換原則绿饵,可以很方便地進(jìn)行策略擴(kuò)展;
缺點(diǎn)
- 策略類數(shù)量增多瓶颠,且所有策略類都必須對(duì)外暴露拟赊,以便客戶端能進(jìn)行選擇;
使用場(chǎng)景
- 針對(duì)同一類型問(wèn)題粹淋,有多種處理方式吸祟,每一種都能獨(dú)立解決問(wèn)題瑟慈;
- 算法需要自由切換的場(chǎng)景;
- 需要屏蔽算法規(guī)則的場(chǎng)景屋匕;
模式講解
首先來(lái)看下 策略模式 的通用 UML 類圖:
從 UML 類圖中葛碧,我們可以看到,策略模式 主要包含三種角色:
- 上下文角色(Context):用來(lái)操作策略的上下文環(huán)境过吻,屏蔽高層模塊(客戶端)對(duì)策略进泼,算法的直接訪問(wèn),封裝可能存在的變化纤虽;
- 抽象策略角色(Strategy):規(guī)定策略或算法的行為乳绕;
- 具體策略角色(ConcreteStrategy):具體的策略或算法實(shí)現(xiàn);
以下是 策略模式 的通用代碼:
class Client {
public static void main(String[] args) {
//選擇一個(gè)具體策略
IStrategy strategy = new ConcreteStrategyA();
//來(lái)一個(gè)上下文環(huán)境
Context context = new Context(strategy);
//客戶端直接讓上下文環(huán)境執(zhí)行算法
context.algorithm();
}
//抽象策略類 Strategy
interface IStrategy {
void algorithm();
}
//具體策略類 ConcreteStrategy
static class ConcreteStrategyA implements IStrategy {
@Override
public void algorithm() {
System.out.println("Strategy A");
}
}
//具體策略類 ConcreteStrategy
static class ConcreteStrategyB implements IStrategy {
@Override
public void algorithm() {
System.out.println("Strategy B");
}
}
//上下文環(huán)境
static class Context {
private IStrategy mStrategy;
public Context(IStrategy strategy) {
this.mStrategy = strategy;
}
public void algorithm() {
this.mStrategy.algorithm();
}
}
}
舉個(gè)例子
例子:假設(shè)現(xiàn)在有兩個(gè)數(shù)與一個(gè)運(yùn)算符逼纸,要求使用該運(yùn)算符操作這兩個(gè)數(shù)洋措。
分析:直接思路:通過(guò)判斷運(yùn)算符符號(hào),對(duì)這兩個(gè)數(shù)進(jìn)行運(yùn)算杰刽。代碼如下所示:
static class Calculator {
private static final String SYMBOL_ADD = "+";
private static final String SYMBOL_SUB = "-";
public int calc(int a, int b, final String symbol) {
int result = 0;
if (SYMBOL_ADD.equals(symbol)) {
result = a + b;
} else if (SYMBOL_SUB.equals(symbol)) {
result = a - b;
}
return result;
}
}
但是這樣寫的話菠发,如果我們現(xiàn)在要擴(kuò)展乘法*
或除法/
運(yùn)算,那么就要在calc
方法內(nèi)增加對(duì)應(yīng)的if...else
判斷贺嫂,代碼臃腫并且擴(kuò)展性太低雷酪。
而如果采用策略模式涝婉,將各種運(yùn)算符的計(jì)算都?xì)w并到對(duì)應(yīng)具體策略,這樣吩跋,就能簡(jiǎn)化代碼并且?guī)?lái)很好的擴(kuò)展性渔工,具體代碼如下:
class Client {
public static void main(String[] args) {
ICalculator calculator = new Add();
Context context = new Context(calculator);
int result = context.calc(1,2);
System.out.println(result);
}
interface ICalculator {
int calc(int a, int b);
}
static class Add implements ICalculator {
@Override
public int calc(int a, int b) {
return a + b;
}
}
static class Sub implements ICalculator {
@Override
public int calc(int a, int b) {
return a - b;
}
}
static class Multi implements ICalculator {
@Override
public int calc(int a, int b) {
return a * b;
}
}
static class Divide implements ICalculator {
@Override
public int calc(int a, int b) {
return a / b;
}
}
static class Context {
private ICalculator mCalculator;
public Context(ICalculator calculator) {
this.mCalculator = calculator;
}
public int calc(int a, int b) {
return this.mCalculator.calc(a, b);
}
}
}
從上面代碼中梁丘,我們可以看到旺韭,我們完全消除了對(duì)運(yùn)算符號(hào)進(jìn)行判斷的哪些if...else
的冗余代碼区端,取而代之的是客戶端直接決定使用哪種算法,然后交由上下文獲取結(jié)果杨何。并且上面代碼中我們還擴(kuò)展了乘法Multi
和除法Divide
運(yùn)算,所需要做的就只是擴(kuò)展相應(yīng)的策略類而已危虱。
注意:策略模式 中的上下文環(huán)境(Context)埃跷,其職責(zé)本來(lái)是隔離客戶端與策略類的耦合,讓客戶端完全與上下文環(huán)境溝通,無(wú)需關(guān)系具體策略近弟。但是從上面的代碼中我們可以看到祷愉,客戶端內(nèi)部直接自己指定要哪種策略(ICalculator calculator = new Add()
),客戶端與具體策略類耦合了赴涵,而上下文環(huán)境在這里其的作用只是負(fù)責(zé)調(diào)度執(zhí)行订讼,獲取結(jié)果,并沒(méi)有完全起到隔離客戶端與策略類的作用寄纵。一般可以通過(guò)簡(jiǎn)單工廠模式將具體策略的創(chuàng)建與客戶端進(jìn)行隔離程拭,或者是通過(guò) 策略枚舉 將上下文環(huán)境與具體策略類融合在一起棍潘,簡(jiǎn)化代碼。當(dāng)具體策略相對(duì)穩(wěn)定時(shí)恤浪,推薦使用 策略枚舉 簡(jiǎn)化代碼资锰,具體代碼如下:
class EnumClient {
public static void main(String[] args) {
int result = Calculator.ADD.calc(1, 2);
System.out.println(result);
// System.out.println(Calculator.ADD.getSymbol());
}
static enum Calculator {
// 加法運(yùn)算
ADD("+") {
@Override
public int calc(int a, int b) {
return a + b;
}
},
SUB("-") {
@Override
public int calc(int a, int b) {
return a - b;
}
};
private String symbol;
private Calculator(String symbol) {
this.symbol = symbol;
}
public String getSymbol() {
return this.symbol;
}
public abstract int calc(int a, int b);
}
}