日本做受視頻網(wǎng)站鄭州百度推廣開戶
前言
策略模式是平時Java開發(fā)中常用的一種,雖然已有很多講解設計模式的文章,但是這里還是寫篇文章來從自己理解的角度講解一下。
使用場景
我們不妨進行場景假設,要對我們的軟件進行授權(quán)管理:在啟動我們的軟件之前先要校驗是否存在合法的授權(quán),如果授權(quán)不合法則要求用戶進行激活操作。作為例子,我們就簡單地實現(xiàn)一下授權(quán)校驗功能:分發(fā)的授權(quán)文件中內(nèi)容是一個四位隨機數(shù),并且最后一位是數(shù)字且為0。我們只要校驗授權(quán)文件中內(nèi)容的最后一位是數(shù)字0即可。
public class LicenseService {public boolean checkLicense() {boolean result = false;// abc0File file = Path.of("./secret").toFile();String content = "";try{// 讀取文件內(nèi)容BufferedReader br = new BufferedReader(new FileReader(file));content = br.readLine();br.close();}catch(Exception e){e.printStackTrace();}// 末尾字符是0即認為校驗通過if (content.endsWith("0")) {result = true;}return result;}
}
需求變更
現(xiàn)在需求進行了變更,不再校驗末尾字符為0了,而是校驗開頭字符是0,因此我們需要對程序進行修改。并且,我們在調(diào)整程序的過程中將讀取文件內(nèi)容和授權(quán)校驗的邏輯進行分離,將授權(quán)校驗的邏輯抽到一個單獨的方法中。
public boolean checkLicense() {...result = checkInternal(content, result);...}private static boolean checkInternal(String content, boolean result) {if (content.startsWith("0")) {result = true;}return result;}
改完之后又接到了最新通知,還有可能改回原來末尾字符的判斷方式,于是我們又對方法進行了調(diào)整。通過方法傳入一個參數(shù)來決定使用哪種方式判斷:
public boolean checkLicense() {...result = checkInternal(content, result, 1);...}private static boolean checkInternal(String content, boolean result, int choose) {// 通過方法傳入的choose來決定使用哪種算法if (choose == 0) {if (content.endsWith("0")) {result = true;}} else if (choose == 1) {if (content.startsWith("0")) {result = true;}}return result;}
策略模式
上面我們的例子是比較簡單的,但是達到了演示的效果:校驗授權(quán)的實現(xiàn)可能有多個版本,并且不同版本的實現(xiàn)都有可能被使用。為了后續(xù)方便擴展和維護,我們把checkInternal方法中的兩個if判斷中的邏輯再抽離出來。
首先定義一個策略接口:
public interface CheckStrategy {boolean check(String content);
}
然后將兩個if中的邏輯轉(zhuǎn)到接口的實現(xiàn)類中:
public class CheckStart implements CheckStrategy {@Overridepublic boolean check(String content) {boolean result = false;if (content.startsWith("0")) {result = true;}return result;}
}
public class CheckEnd implements CheckStrategy {@Overridepublic boolean check(String content) {boolean result = false;if (content.endsWith("0")) {result = true;}return result;}
}
接下來再調(diào)整一下LicenseService
中方法的調(diào)用,把原來的checkInternal
方法中的if
語句進行調(diào)整,改為調(diào)用CheckStrategy
中的方法:
public boolean checkLicense() {...result = checkInternal(content, new CheckStart());...
}private static boolean checkInternal(String content, CheckStrategy strategy) {return strategy.check(content);
}
更多思考
有說一種對于策略的說法是替換滿屏的if else
,我認為這不能算是策略模式的使用目的,只能算是應用了策略模式后的副產(chǎn)物。
它更多的使用場景是這樣:有某一大方法,其中的一個環(huán)節(jié)可以有不同實現(xiàn),并且進行環(huán)節(jié)的算法替換時不影響原大方法的功能(或受到預期的控制)。這里再舉一些應用場景的例子,不夠精準但重在體會其中的思想:
- 實現(xiàn)游戲可以用不同的底層引擎,引擎之間的替換可以應用策略模式
- 程序和數(shù)據(jù)庫交互時可能用到不同的數(shù)據(jù)庫產(chǎn)品如mysql、sqllite,對不同數(shù)據(jù)庫的交互操作可以應用策略模式
- 別的我暫時想不起來了
Spring中實戰(zhàn)
現(xiàn)在java web應用里Spring算是事實上的標準了,在Spring中用策略模式還是有一些小技巧的。下面直接給出代碼請同學們品。
@Service
public class LicenseService {// 注入strategy manager@Autowiredprivate StrategyManager strategyManager;public boolean checkLicense() {boolean result = false;// abc0File file = Path.of("./secret").toFile();String content = "";try {// 讀取文件內(nèi)容BufferedReader br = new BufferedReader(new FileReader(file));content = br.readLine();br.close();} catch (Exception e) {e.printStackTrace();}// 由manager作為策略類實現(xiàn)的提供者result = strategyManager.pickCheckStrategy(CheckStrategyEnum.START.toString()).check(content);return result;}
}
@Service
public class StrategyManager {// 注入CheckStrategy list@Autowiredprivate List<CheckStrategy> checkStrategyList;public CheckStrategy pickCheckStrategy(String type) {// 根據(jù)傳入的type從上面list中取出對應的策略實現(xiàn)類并返回給調(diào)用者return checkStrategyList.stream().filter(s -> s.type().equals(type)).findFirst().orElseThrow();}
}enum CheckStrategyEnum {END, START;
}
public interface CheckStrategy {/*** 返回策略實現(xiàn)類的類型,用于為manager提供實現(xiàn)類的標識** @return 自定義的枚舉類型 {@link CheckStrategyEnum}*/String type();/*** 判斷授權(quán)。算法由實現(xiàn)類確定** @param content 判斷的內(nèi)容* @return 是否判斷成功*/boolean check(String content);
}
@Service
public class CheckStart implements CheckStrategy {@Overridepublic String type() {// 返回對應的枚舉typereturn CheckStrategyEnum.END.toString();}@Overridepublic boolean check(String content) {boolean result = false;if (content.startsWith("0")) {result = true;}return result;}
}
@Service
public class CheckEnd implements CheckStrategy {@Overridepublic String type() {// 返回對應的枚舉typereturn CheckStrategyEnum.START.toString();}@Overridepublic boolean check(String content) {boolean result = false;if (content.endsWith("0")) {result = true;}return result;}
}