一文簡單全面了解策略模式的使用【花幾分鐘輕松掌握一個知識點(diǎn)】
文章目錄
問題需求(支付系統(tǒng)渠道商的選擇問題)
問題解決
V1.0版本
v1.1版本
UML圖
策略模式
定義
主要作用
優(yōu)點(diǎn)
缺點(diǎn)
策略模式與其它模式的比較
與狀態(tài)模式的比較
與簡單工廠模式的比較
擴(kuò)展延伸
v1.2版本
總結(jié)
問題需求(支付系統(tǒng)渠道商的選擇問題)
聚合支付項(xiàng)目對接了三個渠道商,分別渠道商A,渠道商B,渠道商C,每個渠道商都有一套獨(dú)立的對接文檔(從商戶入駐,到支付,到清算)。那么該如何實(shí)現(xiàn)這個功能呢?
問題解決
V1.0版本
V1.0版本就是直接梭哈,將所有的業(yè)務(wù)邏輯寫在了客戶端,客戶端在調(diào)用的時候可以分別調(diào)用各個渠道商的商戶入駐方法。
public class Client {
public static void main(String[] args) {
Client client = new Client();
client.addCustomerA(“商戶1”);
client.addCustomerB(“商戶2”);
client.addCustomerC(“商戶3”);
}
public String addCustomerA(String customer) {
System.out.println("渠道商A入駐"+customer+"成功");
return "success";
}
public String addCustomerB(String customer) {
System.out.println("渠道商B入駐"+customer+"成功");
return "success";
}
public String addCustomerC(String customer) {
System.out.println("渠道商C入駐"+customer+"成功");
return "success";
}
}
上述代碼把策略和調(diào)用都放在了客戶端中,從中不難看出,如果要在增加一個渠道商的話,則必定要修改客戶端
增加新渠道商的商戶入駐方法。這樣就會導(dǎo)致客戶端變得十分臃腫復(fù)雜維護(hù)起來非常麻煩,同時也沒有體現(xiàn)面向?qū)ο蟮脑O(shè)計(jì)思想,在實(shí)際開發(fā)中寫出這樣的代碼怕是要被老板優(yōu)化。
那么我們該如何優(yōu)化呢?
v1.1版本
從面向?qū)ο蟮慕嵌瘸霭l(fā),每個渠道商抽象成一個具體的對象,用一個單獨(dú)的類保存其屬性和行為,同時公共行為(商戶入駐的行為)則用一個接口來定義,每個渠道商的類分別實(shí)現(xiàn)這個接口。
定義公共的接口Income,該接口主要是規(guī)范了各個渠道商的行為,這里通過addCustomer方法定義了商戶入駐的行為。
public interface Income {
/**
* 商戶入駐
* @return
*/
String addCustomer(String customer);
}
定義各個渠道商的類,這里分別定義了MerchantAIncome,MerchantBIncome以及MerchantCIncome 三個渠道商的類,并且這三個類都實(shí)現(xiàn)了Income接口的addCustomer方法,實(shí)現(xiàn)商戶入駐。
public class MerchantAIncome implements Income {
@Override
public String addCustomer(String customer) {
System.out.println(“渠道商A入駐”+customer+“成功”);
return “success”;
}
}
定義環(huán)境類Context,這里的環(huán)境類主要的作用就是持有一個Income對象的引用,并且該Income對象所能操作的各種行為(即各種策略)。
public class Context {
private Income income;
public Context(Income income) {
this.income = income;
}
public String addCustomer(String customer) {
return income.addCustomer(customer);
}
}
定義調(diào)用客戶端
public class Client {
public static void main(String[] args) {
new Context(new MerchantAIncome()).addCustomer(“商戶1”);
new Context(new MerchantBIncome()).addCustomer(“商戶2”);
new Context(new MerchantCIncome()).addCustomer(“商戶3”);
}
}
UML圖
上述代碼的UML圖如下圖所示:這里有三個主要的角色:
環(huán)境類Context
抽象的策略Income接口
具體的策略MerchantAIncome,MerchantBIncome以及MerchantCIncome這三個類。
如上圖所示:每個渠道商的類都是實(shí)現(xiàn)了一個公共的接口,該接口定義了渠道商中各種行為,包括商戶入駐等等??蛻舳伺袛喈?dāng)前使用的是哪個渠道商。然后,生成該渠道商的實(shí)例,接著調(diào)用該渠道商的相關(guān)方法。每個渠道商自身的變化不會影響到客戶端。相比較于V1.0版本而言,代碼邏輯簡潔了很多,增加一個渠道商的話只需要再增加一個渠道商的類,并且在客戶端中加上一個判斷,符合開閉原則。這里就引出了本文將要介紹的模式----策略模式。
策略模式
定義
策略模式就是定義一系列的算法(對應(yīng)本例中的一系列渠道商), 將每個算法封裝到具有公共接口的一系列策略類中(對應(yīng)到本例中就是將每個渠道商封裝成一個單獨(dú)的類,并且這些類實(shí)現(xiàn)一個公共的接口),從而使它們可以相互替換并且讓算法可以在不影響客戶端的情況下發(fā)生變化。
主要作用
從策略模式的定義中我們可以看出策略模式的主要作用就是將算法的責(zé)任和對象本身解耦,使得:
算法可以獨(dú)立于客戶端而變化(每個渠道商內(nèi)部變化不影響客戶端)
客戶端可以根據(jù)外部條件而選擇不同的策略來解決不同的問題。
優(yōu)點(diǎn)
策略類之間可以自由切換
易于擴(kuò)展,增加一個新的策略只需要添加一個具體的策略類即可,基本不需要改變原有的代碼,符合“開閉原則”
消除了一些 if else條件語句
缺點(diǎn)
客戶端必須知道所有的策略類,并自行決定使用哪個策略類。
策略模式將造成產(chǎn)生很多策略類,可以通過使用享元模式在一定程度上減少對象的數(shù)量。
策略模式與其它模式的比較
與狀態(tài)模式的比較
策略模式的條件選擇只執(zhí)行一次,而狀態(tài)模式是隨著實(shí)例參數(shù)(對象實(shí)例的狀態(tài))的改變不停地更改執(zhí)行模式。
與簡單工廠模式的比較
工廠模式是創(chuàng)建型模式,它關(guān)注的是對象的創(chuàng)建,提供創(chuàng)建對象的接口,
策略模式是對象行為型模式,它關(guān)注的是行為和算法的封裝。比如:出行方案中,策略模式是讓你選擇一種出行方式,而工廠模式是代替你構(gòu)建具體的方案。
擴(kuò)展延伸
v1.2版本
說完了策略模式,前面V1.1版本確實(shí)很好的運(yùn)用到了策略模式,還是還不夠,還是有點(diǎn)繁瑣,客戶端在調(diào)用的時候還是需要通過條件判斷來決定使用哪種策略,這樣還是沒有干掉if和else,那么有沒有什么方式可以干掉客戶端的條件判斷呢?答案是有的。我們可以通過枚舉類來定義好各種渠道商的實(shí)例。
這里只需要新增一個枚舉類
public enum MerchantIncomeEnum {
MERCHANT_A("a", new MerchantAIncome()),
MERCHANT_B("b", new MerchantBIncome()),
MERCHANT_C("c", new MerchantCIncome()),;
private String key;
private Income income;
MerchantIncomeEnum(String key, Income income) {
this.key = key;
this.income = income;
}
public static Income getIncome(String key) {
Map<String, Income> map = Arrays.stream(MerchantIncomeEnum.values()).collect(Collectors.toMap(p -> p.key, p -> p.income));
return map.get(key);
}
}
修改環(huán)境類Context
public class Context {
public String addCustomer(String key, String customer) {
Income income = MerchantIncomeEnum.getIncome(key);
return income.addCustomer(customer);
}
}
這樣客戶端在調(diào)用的時候只需要傳入指定渠道商的Key就可以調(diào)用指定渠道商的方法。
總結(jié)
本文由一個實(shí)際應(yīng)用場景出發(fā),引出了策略模式:策略模式主要解決的問題是如何將對象和算法分開,使得算法可以獨(dú)立于使用它的客戶端而變化。它的優(yōu)點(diǎn)是易于擴(kuò)展,策略類之間可以自由的切換。缺點(diǎn)是客戶端在調(diào)用的時候必須要知道所有的策略類,并自行決定使用哪個策略類。同時策略模式會產(chǎn)生很多策略類。策略模式的適用于有一系列算法或者策略的場景下,比如:商場的各種折扣算法,出行的各種方案等等。
作者:碼農(nóng)飛哥
微信公眾號:碼農(nóng)飛哥
![](https://img-blog.csdnimg.cn/20210425103418854.jpg?)