新聞中心
前言
在學(xué)開(kāi)發(fā)的第二年就開(kāi)始聽(tīng)說(shuō)要想代碼寫得好,一定要會(huì)設(shè)計(jì)模式。于是就興致沖沖的啃了《Head First 設(shè)計(jì)模式》,看完之后對(duì)于策略模式映像很深刻,覺(jué)得這個(gè)模式好,易上手,應(yīng)用廣,我又能優(yōu)化一波代碼了,于是興致沖沖的打開(kāi)了我的 IDEA,開(kāi)整?。。?/p>

10年積累的做網(wǎng)站、網(wǎng)站制作經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶對(duì)網(wǎng)站的新想法和需求。提供各種問(wèn)題對(duì)應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先網(wǎng)站設(shè)計(jì)后付款的網(wǎng)站建設(shè)流程,更有古縣免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
策略模式初體驗(yàn)(錯(cuò)誤示范)
在講訴我的策略模式首秀前,我們先回顧下策略模式的基本概念。
策略模式
- 意圖:定義一系列的算法,把它們一個(gè)個(gè)封裝起來(lái), 并且使它們可相互替換。
- 主要解決:在有多種算法相似的情況下,使用 if…else 所帶來(lái)的復(fù)雜和難以維護(hù)。
- 何時(shí)使用:一個(gè)系統(tǒng)有許多許多類,而區(qū)分它們的只是他們直接的行為。
簡(jiǎn)單的來(lái)說(shuō)當(dāng)做某個(gè)事情有多個(gè)方式的時(shí)候,可以抽象為接口,然后每個(gè)實(shí)現(xiàn)是一種解決方式,由調(diào)用方來(lái)選擇不同的實(shí)現(xiàn)方式。
理解了后我開(kāi)始對(duì)我們的代碼進(jìn)行了重構(gòu),當(dāng)時(shí)我第一家公司有這樣一段代碼,大概是這個(gè)意思(時(shí)間長(zhǎng)了,我憑記憶重寫的)。
有這樣一個(gè)抽獎(jiǎng)的方法,我們后臺(tái)控制中獎(jiǎng)率,不同的時(shí)候我們會(huì)調(diào)整不同的中獎(jiǎng)策略。
public class NumStrategy {
enum RandomEnum{
/**
* 平均策略
*/
AVERAGE,
/**
* 80%的幾率中獎(jiǎng)
*/
RANDOM28;
}
/**
* 抽獎(jiǎng)方法,根據(jù)不同的策略進(jìn)行抽獎(jiǎng)
* @param randomEnum
* @return true:代表中獎(jiǎng) false:代表沒(méi)中獎(jiǎng)
*/
public boolean luckDraw(RandomEnum randomEnum){
if(randomEnum.equals(RandomEnum.AVERAGE)){
Random random = new Random();
int num = random.nextInt(100);
return num >= 50;
}else if(randomEnum.equals(RandomEnum.RANDOM28)){
Random random = new Random();
int num = random.nextInt(100);
return num >= 20;
}
return false;
}
}我一看,這不就是妥妥的策略模式嗎?開(kāi)搞。
一頓改造之后變成了這樣:
public class NumStrategy2 {
enum RandomEnum{
/**
* 平均策略
*/
AVERAGE,
/**
* 80%的幾率中獎(jiǎng)
*/
RANDOM28;
}
/**
* 抽獎(jiǎng)方法,根據(jù)不同的策略進(jìn)行抽獎(jiǎng)
* @param randomEnum
* @return
public boolean luckDraw(RandomEnum randomEnum){
if(randomEnum.equals(RandomEnum.AVERAGE)){
return new AverageStrategy().luckDraw();
}else if(randomEnum.equals(RandomEnum.RANDOM28)){
return new Random28Strategy().luckDraw();
}
return false;
}
interface LuckDrawStrategy{
boolean luckDraw();
}
class AverageStrategy implements LuckDrawStrategy{
@Override
public boolean luckDraw(){
Random random = new Random();
int num = random.nextInt(100);
return num >= 50;
}
}
class Random28Strategy implements LuckDrawStrategy{
@Override
public boolean luckDraw(){
Random random = new Random();
int num = random.nextInt(100);
return num >= 20;
}
}
}改造完成之后我滿意的提交了代碼,但是在組長(zhǎng) review 的時(shí)候給我又改了回來(lái)。說(shuō)你整這么多類干嘛?我理直氣壯的說(shuō)我這是用策略模式優(yōu)化代碼。他說(shuō)沒(méi)必要,先改回去吧。
我憤憤的接受了,但心里想著:哎,你連策略模式都不懂?
經(jīng)過(guò)這么多年,我開(kāi)始理解我當(dāng)時(shí)的做法其實(shí)不對(duì),本來(lái)很簡(jiǎn)單的代碼,而且里面的邏輯不會(huì)有變動(dòng),其實(shí)不需要抽象出來(lái)。我的改動(dòng)有過(guò)度設(shè)計(jì)之嫌。把原來(lái)的30行代碼搞成了80行
一報(bào)還一報(bào),這幾年我見(jiàn)過(guò)太多次當(dāng)年的我這樣寫代碼的了。
即為了用設(shè)計(jì)模式而用設(shè)計(jì)模式。而忘了設(shè)計(jì)模式的初衷是為了代碼更易理解,更可靠,更易維護(hù)。
甚至還見(jiàn)過(guò)有人學(xué)了策略模式后說(shuō)要把項(xiàng)目里所有的 if/else都安排上策略模式。
梅開(kāi)二度
又過(guò)了一年多,在一次面試的時(shí)候,也有著關(guān)于策略模式的討論。
【面試官】問(wèn):你說(shuō)你用過(guò)策略模式,請(qǐng)問(wèn)你為什么用它?
【我】:為了抽離各個(gè)不同實(shí)現(xiàn)邏輯,優(yōu)化 if/else,使代碼更簡(jiǎn)單易懂
【面試官】:你具體說(shuō)說(shuō),怎么去掉的 if/else
【我】:內(nèi)心 OS(背的知識(shí)點(diǎn),我也好久沒(méi)用了?。?。我硬著頭皮說(shuō),我可以使用工廠模式+策略模式來(lái)做。
【面試官】:那你工廠模式的那里不是也要用 if/else 判斷嗎?
【我】:。。。額。唔。。。那確實(shí)還是要用到 if/else
把我問(wèn)住了,我支支吾吾的回答確實(shí)還是要 if/else 來(lái)判斷一次,只不過(guò)把判斷移到了工廠模式里面去了。
我下來(lái)后又去實(shí)踐了下,想著放在 map 里行不行呢?
public class NumStrategy3 {
enum RandomEnum{
/**
* 平均策略
*/
AVERAGE,
/**
* 80%的幾率中獎(jiǎng)
*/
RANDOM28;
}
static Map map = new HashMap<>();
static{
map.put(RandomEnum.RANDOM28,new Random28Strategy());
map.put(RandomEnum.AVERAGE,new AverageStrategy());
}
/**
* 抽獎(jiǎng)方法,根據(jù)不同的策略進(jìn)行抽獎(jiǎng)
* @param randomEnum
* @return
public boolean luckDraw(RandomEnum randomEnum){
LuckDrawStrategy luckDrawStrategy = map.get(randomEnum);
return luckDrawStrategy.luckDraw();
}
interface LuckDrawStrategy{
boolean luckDraw();
}
static class AverageStrategy implements LuckDrawStrategy{
@Override
public boolean luckDraw(){
Random random = new Random();
int num = random.nextInt(100);
return num >= 50;
}
}
static class Random28Strategy implements LuckDrawStrategy{
@Override
public boolean luckDraw(){
Random random = new Random();
int num = random.nextInt(100);
return num >= 20;
}
}
} 終于是解決了 if/else 的情況,不過(guò)這樣很短的 if/else,里面邏輯不怎么變動(dòng)時(shí),我個(gè)人是不建議用策略模式,這里只是示例。
推薦用法
又過(guò)了幾年,當(dāng)初的菜鳥(niǎo)也成長(zhǎng)為了一個(gè)老鳥(niǎo)。
當(dāng)時(shí)項(xiàng)目里有這樣一個(gè)代碼:
下面的代碼我進(jìn)行了一些簡(jiǎn)化,我們有一個(gè)功能,對(duì)頁(yè)面上的指標(biāo)進(jìn)行計(jì)算,不同的指標(biāo)對(duì)應(yīng)不同的計(jì)算方法。頁(yè)面上指標(biāo)一期做 4 個(gè),后續(xù)會(huì)做到十幾個(gè)。
public interface TransferService {
String transfer();
}
@Service
public class SearchTransformService {
@Autowired
private UserTransferService userTransferService;
@Autowired
private AgeTransferService ageTransferService;
@Autowired
private InterestTransferService interestTransferService;
/**
* 根據(jù)不同的編碼進(jìn)行轉(zhuǎn)換
* @param code
* @return
public String transform(String code){
if(code.equals("user")){
return userTransferService.transfer();
}else if(code.equals("age")){
return ageTransferService.transfer();
}else if(code.equals("interest")){
return interestTransferService.transfer();
}
return "";
}
}可以看到這樣的業(yè)務(wù)場(chǎng)景下,這樣的寫法 if/else 就會(huì)很長(zhǎng),后續(xù)十幾個(gè)的情況下就很難維護(hù)。另外 code 使用的是魔數(shù),也是不好的一種寫法。我對(duì)此進(jìn)行了優(yōu)化如下:
先將 code 用枚舉定義
enum CodeEnum {
USER("user"),
AGE("age"),
INTEREST("interest"),
;
private String code;
public String getCode() {
return code;
}
CodeEnum(String code) {
this.code = code;
}
private static final Map map = Arrays.stream(CodeEnum.values()).collect(Collectors.toMap(CodeEnum::getCode, Function.identity()));
public CodeEnum of(String code) {
return map.get(code);
}
} 原有的接口上增加一個(gè)transCode方法,每個(gè)實(shí)現(xiàn)需要聲明是對(duì)應(yīng)哪個(gè)編碼的實(shí)現(xiàn)
public interface TransferService {
String transfer();
CodeEnum transCode();
}
@Service
public class AgeTransferService implements TransferService {
@Override
public String transfer(){
return null;
}
@Override
public CodeEnum transCode(){
return CodeEnum.AGE;
}
}使用 map 存儲(chǔ)編碼對(duì)應(yīng)的實(shí)現(xiàn)類的關(guān)聯(lián)關(guān)系,以此來(lái)獲取對(duì)應(yīng)的轉(zhuǎn)換器實(shí)現(xiàn)類
@Service
public class SearchTransformService implements InitializingBean {
@Autowired
private ListtransferServiceList;
private MaptransferServiceMap;
@Override
// 項(xiàng)目啟動(dòng)時(shí)將實(shí)現(xiàn)類放入到map中去
public void afterPropertiesSet() throws Exception {
transferServiceMap = transferServiceList.stream().collect(Collectors.toMap(TransferService::transCode, Function.identity()));
}
/**
* 根據(jù)不同的編碼進(jìn)行轉(zhuǎn)換
* @param code
* @return
public String transform(String code){
TransferService transferService = transferServiceMap.get(CodeEnum.of(code));
Assert.notNull(transferService,"找不到對(duì)應(yīng)的轉(zhuǎn)換器");
return transferService.transfer();
}
}
重構(gòu)后是不是就很簡(jiǎn)潔了呢?如果后續(xù)新增新的編碼轉(zhuǎn)換器,只需要先在枚舉里定義,然后新增實(shí)現(xiàn)類實(shí)現(xiàn)方法就行了,不需要關(guān)心是怎么調(diào)用的,只關(guān)心具體的實(shí)現(xiàn)邏輯,降低了維護(hù)成本。
這才是策略模式的真正應(yīng)用吧。不要再亂用了,哈哈哈。
網(wǎng)站標(biāo)題:知道策略模式!但不會(huì)在項(xiàng)目里使用?
網(wǎng)頁(yè)URL:http://m.fisionsoft.com.cn/article/dhcoocs.html


咨詢
建站咨詢
