前言
策略模式是平時(shí)Java開發(fā)中常用的一種,雖然已有很多講解設(shè)計(jì)模式的文章,但是這里還是寫篇文章來(lái)從自己理解的角度講解一下。
使用場(chǎng)景
我們不妨進(jìn)行場(chǎng)景假設(shè),要對(duì)我們的軟件進(jìn)行授權(quán)管理:在啟動(dòng)我們的軟件之前先要校驗(yàn)是否存在合法的授權(quán),如果授權(quán)不合法則要求用戶進(jìn)行激活操作。作為例子,我們就簡(jiǎn)單地實(shí)現(xiàn)一下授權(quán)校驗(yàn)功能:分發(fā)的授權(quán)文件中內(nèi)容是一個(gè)四位隨機(jī)數(shù),并且最后一位是數(shù)字且為0。我們只要校驗(yàn)授權(quán)文件中內(nèi)容的最后一位是數(shù)字0即可。
public class LicenseService {
public boolean checkLicense() {
boolean result = false;
// abc0
File 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即認(rèn)為校驗(yàn)通過(guò)
if (content.endsWith("0")) {
result = true;
}
return result;
}
}
需求變更
現(xiàn)在需求進(jìn)行了變更,不再校驗(yàn)?zāi)┪沧址麨?了,而是校驗(yàn)開頭字符是0,因此我們需要對(duì)程序進(jìn)行修改。并且,我們?cè)谡{(diào)整程序的過(guò)程中將讀取文件內(nèi)容和授權(quán)校驗(yàn)的邏輯進(jìn)行分離,將授權(quán)校驗(yàn)的邏輯抽到一個(gè)單獨(dú)的方法中。
public boolean checkLicense() {
...
result = checkInternal(content, result);
...
}
private static boolean checkInternal(String content, boolean result) {
if (content.startsWith("0")) {
result = true;
}
return result;
}
改完之后又接到了最新通知,還有可能改回原來(lái)末尾字符的判斷方式,于是我們又對(duì)方法進(jìn)行了調(diào)整。通過(guò)方法傳入一個(gè)參數(shù)來(lái)決定使用哪種方式判斷:
public boolean checkLicense() {
...
result = checkInternal(content, result, 1);
...
}
private static boolean checkInternal(String content, boolean result, int choose) {
// 通過(guò)方法傳入的choose來(lái)決定使用哪種算法
if (choose == 0) {
if (content.endsWith("0")) {
result = true;
}
} else if (choose == 1) {
if (content.startsWith("0")) {
result = true;
}
}
return result;
}
策略模式
上面我們的例子是比較簡(jiǎn)單的,但是達(dá)到了演示的效果:校驗(yàn)授權(quán)的實(shí)現(xiàn)可能有多個(gè)版本,并且不同版本的實(shí)現(xiàn)都有可能被使用。為了后續(xù)方便擴(kuò)展和維護(hù),我們把checkInternal方法中的兩個(gè)if判斷中的邏輯再抽離出來(lái)。
首先定義一個(gè)策略接口:
public interface CheckStrategy {
boolean check(String content);
}
然后將兩個(gè)if中的邏輯轉(zhuǎn)到接口的實(shí)現(xiàn)類中:
public class CheckStart implements CheckStrategy {
@Override
public boolean check(String content) {
boolean result = false;
if (content.startsWith("0")) {
result = true;
}
return result;
}
}
public class CheckEnd implements CheckStrategy {
@Override
public boolean check(String content) {
boolean result = false;
if (content.endsWith("0")) {
result = true;
}
return result;
}
}
接下來(lái)再調(diào)整一下LicenseService
中方法的調(diào)用,把原來(lái)的checkInternal
方法中的if
語(yǔ)句進(jìn)行調(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);
}
更多思考
有說(shuō)一種對(duì)于策略的說(shuō)法是替換滿屏的if else
,我認(rèn)為這不能算是策略模式的使用目的,只能算是應(yīng)用了策略模式后的副產(chǎn)物。
它更多的使用場(chǎng)景是這樣:有某一大方法,其中的一個(gè)環(huán)節(jié)可以有不同實(shí)現(xiàn),并且進(jìn)行環(huán)節(jié)的算法替換時(shí)不影響原大方法的功能(或受到預(yù)期的控制)。這里再舉一些應(yīng)用場(chǎng)景的例子,不夠精準(zhǔn)但重在體會(huì)其中的思想:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-830455.html
- 實(shí)現(xiàn)游戲可以用不同的底層引擎,引擎之間的替換可以應(yīng)用策略模式
- 程序和數(shù)據(jù)庫(kù)交互時(shí)可能用到不同的數(shù)據(jù)庫(kù)產(chǎn)品如mysql、sqllite,對(duì)不同數(shù)據(jù)庫(kù)的交互操作可以應(yīng)用策略模式
- 別的我暫時(shí)想不起來(lái)了
Spring中實(shí)戰(zhàn)
現(xiàn)在java web應(yīng)用里Spring算是事實(shí)上的標(biāo)準(zhǔn)了,在Spring中用策略模式還是有一些小技巧的。下面直接給出代碼請(qǐng)同學(xué)們品。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-830455.html
@Service
public class LicenseService {
// 注入strategy manager
@Autowired
private StrategyManager strategyManager;
public boolean checkLicense() {
boolean result = false;
// abc0
File 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作為策略類實(shí)現(xiàn)的提供者
result = strategyManager
.pickCheckStrategy(CheckStrategyEnum.START.toString())
.check(content);
return result;
}
}
@Service
public class StrategyManager {
// 注入CheckStrategy list
@Autowired
private List<CheckStrategy> checkStrategyList;
public CheckStrategy pickCheckStrategy(String type) {
// 根據(jù)傳入的type從上面list中取出對(duì)應(yīng)的策略實(shí)現(xiàn)類并返回給調(diào)用者
return checkStrategyList
.stream()
.filter(s -> s.type().equals(type))
.findFirst()
.orElseThrow();
}
}
enum CheckStrategyEnum {
END, START;
}
public interface CheckStrategy {
/**
* 返回策略實(shí)現(xiàn)類的類型,用于為manager提供實(shí)現(xiàn)類的標(biāo)識(shí)
*
* @return 自定義的枚舉類型 {@link CheckStrategyEnum}
*/
String type();
/**
* 判斷授權(quán)。算法由實(shí)現(xiàn)類確定
*
* @param content 判斷的內(nèi)容
* @return 是否判斷成功
*/
boolean check(String content);
}
@Service
public class CheckStart implements CheckStrategy {
@Override
public String type() {
// 返回對(duì)應(yīng)的枚舉type
return CheckStrategyEnum.END.toString();
}
@Override
public boolean check(String content) {
boolean result = false;
if (content.startsWith("0")) {
result = true;
}
return result;
}
}
@Service
public class CheckEnd implements CheckStrategy {
@Override
public String type() {
// 返回對(duì)應(yīng)的枚舉type
return CheckStrategyEnum.START.toString();
}
@Override
public boolean check(String content) {
boolean result = false;
if (content.endsWith("0")) {
result = true;
}
return result;
}
}
到了這里,關(guān)于Java設(shè)計(jì)模式——策略的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!