算是讀書筆記吧
極客時間--設(shè)計(jì)模式之美
什么是策略模式
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
定義一族算法類春寿,將每個算法分別封裝起來颅停,讓它們可以互相替換僚祷。策略模式可以使算法的變化獨(dú)立于使用它們的客戶端(這里的客戶端代指使用算法的代碼)
策略模式對策略的定義宁脊、創(chuàng)建、使用三部分進(jìn)行了解耦
策略模式的作用
策略模式解決的問題
策略模式表面上看是為了避免 if-else 分支判斷邏輯,但更深層次上,還是為了解耦以及控制代碼復(fù)雜度
比如在出門旅游時:路線、交通工具的類型溢吻、天數(shù)、艙位等級果元、餐飲促王、住宿等等。
每個節(jié)點(diǎn)在執(zhí)行時而晒,都需要根據(jù)預(yù)算Type
進(jìn)行不同的操作蝇狼,從而引起大量的if-else
判斷。增加一個策略欣硼,修改一個策略题翰,都有可能牽一發(fā)而動全身。需要對所有狀態(tài)進(jìn)行回測诈胜。
public void departure () {
Int money = 100;
String destination = getDestination(money); //獲取目的地
String vehicles = getVehicles(money); //獲取交通工具
天數(shù)豹障、艙位等級、餐飲焦匈、住宿等等.....
}
private String getDestination (Int money) {
if (num < 1000) {
return "1000塊能去得起的地方";
} else if (num < 5000) {
return "5000塊能去得起的地方";
} else if (num < 10000) {
return "10000塊能去得起的地方";
} else if (num < 20000) {
return "20000塊能去得起的地方";
} else if (num < 40000) {
return "40000塊能去得起的地方";
} else if (num < 80000) {
return "80000塊能去得起的地方";
} else ...
}
private String getVehicles (Int money) {
if (num < 1000) {
return "騎車";
} else if (num < 5000) {
return "火車";
} else if (num < 10000) {
return "火車";
} else if (num < 20000) {
return "飛機(jī)";
} else if (num < 40000) {
return "飛機(jī)";
} else if (num < 80000) {
return "飛機(jī)";
} else ...
}
...后面還有一大堆相關(guān)的方法需要判斷
整個業(yè)務(wù)如圖所示血公,所有的判斷都耦合在業(yè)務(wù)流程內(nèi)部,牽一發(fā)而動全身
使用策略模式
我們可以將某一條件(Type
)下的邏輯缓熟,聚合封裝到具體的策略類中
public class departureStrategy1000 implements Strategy { //1000塊的旅行策略
@Override
public void getDestination() {
return "1000塊能去得起的地方";
}
public void getVehicles() {
return "騎車";
}
天數(shù)累魔、艙位等級、餐飲够滑、住宿等等.....
}
使用策略類后如圖所示垦写,每個的情況被封裝聚合到單個策略類中,相互隔離
所以策略模式的作用主要體現(xiàn)在:
- 解耦策略的定義彰触、創(chuàng)建和使用
控制代碼的復(fù)雜度梯投,讓每個部分都不至于過于復(fù)雜、代碼量過多况毅。 - 讓復(fù)雜框架滿足開閉原則
添加或者修改新策略的時候分蓖,最小化、集中化代碼改動尔许,減少引入 bug 的風(fēng)險么鹤。
策略的定義
策略的定義包含一個策略接口和一組實(shí)現(xiàn)這個接口的策略類。
利用基于接口而非實(shí)現(xiàn)編程的方式味廊,對具體策略進(jìn)行解耦蒸甜。
如下棠耕,策略類ConcreteStrategyA、ConcreteStrategyB在策略接口algorithmInterface的使用上柠新,可以隨意替換昧辽。
public interface Strategy { //定義策略接口
void algorithmInterface();
}
public class ConcreteStrategyA implements Strategy { //實(shí)現(xiàn)策略接口的策略類A
@Override
public void algorithmInterface() {
//具體的算法...
}
}
public class ConcreteStrategyB implements Strategy { //實(shí)現(xiàn)策略接口的策略類B
@Override
public void algorithmInterface() {
//具體的算法...
}
}
策略的創(chuàng)建
通常會通過類型(type)來判斷創(chuàng)建哪個策略來使用。
這里登颓,有兩種創(chuàng)建方式
if-else創(chuàng)建
適用有狀態(tài)的策略類,每次創(chuàng)建一個新的策略類給業(yè)務(wù)方使用
public class StrategyFactory {
public static Strategy getStrategy(String type) {
if (type == null || type.isEmpty()) {
throw new IllegalArgumentException("type should not be empty.");
}
if (type.equals("A")) {
return new ConcreteStrategyA();
} else if (type.equals("B")) {
return new ConcreteStrategyB();
}
return null;
}
}
通過工廠模式里的Map進(jìn)行創(chuàng)建
適用于無狀態(tài)的策略類創(chuàng)建红氯,大家共用一個策略類即可
public class StrategyFactory {
private static final Map<String, Strategy> strategies = new HashMap<>();
static {
strategies.put("A", new ConcreteStrategyA());
strategies.put("B", new ConcreteStrategyB());
}
public static Strategy getStrategy(String type) {
if (type == null || type.isEmpty()) {
throw new IllegalArgumentException("type should not be empty.");
}
return strategies.get(type);
}
}
本質(zhì)上點(diǎn)講框咙,是借助“查表法”,根據(jù) type 查表替代根據(jù) type 分支判斷痢甘。
有狀態(tài)的策略類如何用Map進(jìn)行創(chuàng)建
可以利用閉包的特性喇嘱,將創(chuàng)建的邏輯封裝進(jìn)callback中,然后將callback存進(jìn)Map
策略類的使用
如果使用工廠方法創(chuàng)建策略類塞栅,其實(shí)就和工廠方法相同者铜。
只不過我們從工廠取出來的不再是一個某一個具體類的子類簇。
而是一個實(shí)現(xiàn)了策略接口的類簇放椰。
// 運(yùn)行時動態(tài)確定作烟,根據(jù)配置文件的配置決定使用哪種策略
public class Application {
public static void main(String[] args) throws Exception {
Strategy strategy = null;
StrategyFactory factory = new StrategyFactory();
strategy = factory.getStrategy("A"); //獲取策略類
strategy.algorithmInterface(); //調(diào)用策略接口
//...
}
}
是不是要抹殺所有的if/else
如果 if-else 分支判斷不復(fù)雜、代碼不多砾医,這并沒有任何問題拿撩,畢竟 if-else 分支判斷幾乎是所有編程語言都會提供的語法,存在即有理由如蚜。遵循 KISS 原則压恒,怎么簡單怎么來,就是最好的設(shè)計(jì)错邦。非得用策略模式探赫,搞出 n 多類,反倒是一種過度設(shè)計(jì)