記一次重構,策略模式替換if else
場景
先說一下大概要做的事情。我需要先從資料庫A中篩選出一週的基礎資料,呼叫介面獲取資料庫B中的規則,再做一次資料篩選,然後入庫資料庫B。定時任務每週跑一次。
吶,說到這裡,也大概知道了,在資料篩選那裡,將會有業務判斷。虛擬碼就集中展示那裡。
第1版
考慮時間等各種因素,先實現再說。此處並沒有用到資料庫B中的規則,直接在專案中寫的固定值。
for(CustomerResource customerResource : customerResourceList){
//TODO
//V1
//條件1
if(customerResource.getFactoryPrice().intValue() < -10){
condition1 = true;
}else {
continue;//條件1未滿足,直接忽略
}
//條件2
if(customerResource.getLastWeekSales().intValue() > 100){
condition2 = true;
}else {
continue;//條件2未滿足,直接忽略
}
//條件3
if(factoryDay.get(customerResource.getFactoryCode()) == null ? false : factoryDay.get(customerResource.getFactoryCode()) > 3){
condition3 = true;
}else{condition3 = false;}
//條件4
if(factorySales.get(customerResource.getFactoryCode()) == null ? false :
customerResource.getLastWeekSales().divide(factorySales.get(customerResource.getFactoryCode()),2,BigDecimal.ROUND_HALF_UP).doubleValue() > 1.1){
condition4 = true;
}else{condition4 = false;}
//根據4個條件歸類
}
第2版
等到第1版本實現以後,篩選出的資料基本上已經是想要的資料。但是不利於規則的擴充套件。便於維護,後續在後管系統做頁面。
存在每個工廠的規則數量(目前4條)是一樣,但是規則中比較的數值存在變動,比較存在變動(符號)。
如此一來,第1版本顯然已經不符合需求了。
for(CustomerResource customerResource : customerResourceList){
//獲取工廠的規則
List<CustomerResourceRule> rules = factoryRulesMap.get(customerResource.getFactoryCode());
if(rules == null){//未獲取到該工廠的規則
continue;
}
for(CustomerResourceRule rule : rules){
// V2
//條件1
if(ConditionEnum.CONDITION1.getRuleCode().equals(rule.getRuleCode())){
switch (SymbolEnum.getBySymbol(rule.getSymbol())){
case GT:
condition1 = customerResource.getFactoryPrice().intValue() > Integer.parseInt(rule.getSetValue());
break;
case LT:
condition1 = customerResource.getFactoryPrice().intValue() < Integer.parseInt(rule.getSetValue());
break;
default:break;
}
}
//條件2
if(ConditionEnum.CONDITION2.getRuleCode().equals(rule.getRuleCode())){
switch (SymbolEnum.getBySymbol(rule.getSymbol())){
case GT:
condition2 = customerResource.getLastWeekSales().intValue() > Integer.parseInt(rule.getSetValue());
break;
case LT:
condition2 = customerResource.getLastWeekSales().intValue() > Integer.parseInt(rule.getSetValue());
break;
default:break;
}
}
//條件3
if(ConditionEnum.CONDITION3.getRuleCode().equals(rule.getRuleCode())){
List<FactoryClinkerRatio> ratios = factoryRatioMap.get(customerResource.getFactoryCode());
if(ratios != null){
switch (SymbolEnum.getBySymbol(rule.getSymbol())){
case GT:
List<FactoryClinkerRatio> ratioList = ratios.stream().filter(s -> s.getRatios().doubleValue() > Double.parseDouble(rule.getSetValue())).collect(Collectors.toList());
condition3 = ratioList.size() > 3;
break;
case LT:
List<FactoryClinkerRatio> ratioList1 = ratios.stream().filter(s -> s.getRatios().doubleValue() < Double.parseDouble(rule.getSetValue())).collect(Collectors.toList());
condition3 = ratioList1.size() > 3;
break;
default:break;
}
}
}
//條件4
if(ConditionEnum.CONDITION4.getRuleCode().equals(rule.getRuleCode())){
BigDecimal salePlan = factorySalesMap.get(customerResource.getFactoryCode());
if(salePlan != null){
switch (SymbolEnum.getBySymbol(rule.getSymbol())){
case GT:
condition4 = customerResource.getLastWeekSales().divide(salePlan,2,BigDecimal.ROUND_HALF_UP).doubleValue() > Double.parseDouble(rule.getSetValue());
break;
case LT:
condition4 = customerResource.getLastWeekSales().divide(salePlan,2,BigDecimal.ROUND_HALF_UP).doubleValue() < Double.parseDouble(rule.getSetValue());
break;
default:break;
}
}
}
//根據4個條件歸類
}
}
第3版
第二版的程式碼已經基本實現了功能。那時專案經理告訴我,還有更好的寫法,去了解一下策略模式。
就開始了策略模式的學習之路,邊學習邊實踐。(關於設計模式,後續有空會在設計模式專欄中更新,此處就先不介紹了)。
下圖為本次專案中的策略類圖:
重構步驟
-
輔助類
-
列舉
/** * 條件列舉 * Created by zhanghan_a on 2020/6/10. */ public enum ConditionEnum { CONDITION1("1"), CONDITION2("2"), CONDITION3("3"), CONDITION4("4"); private String ruleCode; ConditionEnum(String ruleCode){ this.ruleCode = ruleCode; } public String getRuleCode(){ return ruleCode; } public static ConditionEnum getConditionByCode(String ruleCode){ for(ConditionEnum conditionEnum : values()){ if(conditionEnum.getRuleCode().equals(ruleCode)){ return conditionEnum; } } return null; } }
/** * 符號列舉 * Created by zhanghan_a on 2020/6/10. */ public enum SymbolEnum { GT(">"), LT("<"), WRONG("wrong"); private String symbol; SymbolEnum(String symbol){ this.symbol = symbol; } public String getSymbol(){ return this.symbol; } public static SymbolEnum getBySymbol(String symbol){ for(SymbolEnum symbolEnum : values()){ if(symbolEnum.getSymbol().equals(symbol)){ return symbolEnum; } } return WRONG;//返回錯誤的比較符號 } }
-
自定義註解
/** * Created by zhanghan_a on 2020/6/12. */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface ConditionTypeAnnotation { ConditionEnum type(); }
-
-
在專案中新建策略包:strategy
-
策略類介面
/** * Created by zhanghan_a on 2020/6/12. */ public interface ConditionStrategy { String check(ConditionParams conditionParams); }
-
策略實現類
@Service @ConditionTypeAnnotation(type = ConditionEnum.CONDITION1) public class FirstCondition implements ConditionStrategy{ @Override public String check(ConditionParams conditionParams) { //判斷 boolean condition = false; //TODO 條件1的判斷 return condition ? ConditionEnum.CONDITION1.getRuleCode() : ""; } }
@Service @ConditionTypeAnnotation(type = ConditionEnum.CONDITION2) public class SecondCondition implements ConditionStrategy { @Override public String check(ConditionParams conditionParams) { //判斷 boolean condition = false; //TODO 條件2的判斷 return condition ? ConditionEnum.CONDITION2.getRuleCode() : ""; } }
@Service @ConditionTypeAnnotation(type = ConditionEnum.CONDITION3) public class ThirdCondition implements ConditionStrategy { @Override public String check(ConditionParams conditionParams) { //判斷 boolean condition = false; //TODO 條件3的判斷 return condition ? ConditionEnum.CONDITION3.getRuleCode() : ""; } }
@Service @ConditionTypeAnnotation(type = ConditionEnum.CONDITION4) public class ForthCondition implements ConditionStrategy { @Override public String check(ConditionParams conditionParams) { //判斷 boolean condition = false; //TODO 條件4的判斷 return condition ? ConditionEnum.CONDITION4.getRuleCode() : ""; } }
-
-
在config資料夾下或util資料夾下新增類
-
新增StrategyContext類
/** * Created by zhanghan_a on 2020/6/12. */ public class StrategyContext { private Map<ConditionEnum,Class> strategyMap; public StrategyContext(Map<ConditionEnum, Class> strategyMap) { this.strategyMap = strategyMap; } public ConditionStrategy getStrategy(ConditionEnum conditionEnum){ if(conditionEnum == null){ return null; } if(strategyMap == null){ return null; } Class clazz = strategyMap.get(conditionEnum); if(clazz == null){ return null; } return (ConditionStrategy)BeanTools.getBean(clazz); } }
-
新增StrategyProcessor類
此處用到了反射工具包。請在
pom.xml
中新增依賴<dependency> <groupId>org.reflections</groupId> <artifactId>reflections</artifactId> <version>0.9.10</version> </dependency>
注意新增
@Component
註解/** * Created by zhanghan_a on 2020/6/12. */ @Component public class StrategyProcessor implements BeanFactoryPostProcessor { private static final String BASE_PACKAGE = "com.**.**.strategy";//此處填寫對應專案的包名即可 @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { Map<ConditionEnum,Class> strategyMap = new HashMap<>(); Reflections reflections = new Reflections(BASE_PACKAGE);//掃描策略包 reflections.getTypesAnnotatedWith(ConditionTypeAnnotation.class).forEach(clazz -> {//尋找ConditionTypeAnnotation註解的類 ConditionEnum conditionEnum = clazz.getAnnotation(ConditionTypeAnnotation.class).type();//獲取對應的條件 strategyMap.put(conditionEnum,clazz);//對映條件對應的策略實現類 }); StrategyContext strategyContext = new StrategyContext(strategyMap); configurableListableBeanFactory.registerSingleton(StrategyContext.class.getName(),strategyContext);//註冊為單例 } }
-
-
在之前的業務實現類中新增strategyContext
@Autowired private StrategyContext strategyContext;
-
替換掉第二版的程式碼
for(CustomerResource customerResource : customerResourceList){ //獲取工廠的規則 List<CustomerResourceRule> rules = factoryRulesMap.get(customerResource.getFactoryCode()); if(rules == null){//未獲取到該工廠的規則 continue; } for(CustomerResourceRule rule : rules){ // V3 ConditionEnum conditionEnum = ConditionEnum.getConditionByCode(rule.getRuleCode());//根據規則程式碼獲取對應的列舉型別 if(conditionEnum == null){ continue; } conditionParams.setRule(rule);//設定條件引數 ConditionStrategy conditionStrategy = strategyContext.getStrategy(conditionEnum);//根據列舉型別獲取對應的實現類 resultList.add(conditionStrategy.check(conditionParams));//每次篩選的條件記錄在resultList //根據4個條件歸類 } }
這樣一來,簡潔了很多,也便於後續的擴充套件。
附
-
小編工作兩年有餘,知識的廣度與深度均不夠,尚在學習中。如果您在閱讀中發現各種錯誤,還望指出及時糾正。
-
本篇只是提供一個思路,畢竟每個專案中的業務場景不同。有了這個思路就足夠了,剩下的只需要你去找幾篇類似的部落格,結合自己的需求,大膽嘗試。
-
當初參考來自於簡書的一篇文章《還在業務中用if else,策略模式瞭解一下》,大概是作者刪除或者設為私密,我留下的連結已經失效,就不在此處分享。還是非常感謝作者給了案例作為參考。再者也感謝專案經理的提點,才有了新的收穫。
-
最後,程式碼的重構優化也是從第一步先實現,再到抽象,封裝一步步的來的。沒有最優,只有更優。逐漸讓程式碼越來越靈活。
相關文章
- if else與策略模式模式
- 策略模式+工廠模式取代if{}else{}模式
- 使用列舉ENUM替換Switch或If-Else
- 記一次正規表示式替換,使用 ideaIdea
- 記一次程式碼重構
- 記一次專案重構
- 利用策略模式優化過多 if else 程式碼模式優化
- 使用策略模式重構電商折扣和支付場景模式
- Swift 中單例模式的替換Swift單例模式
- 還在使用 if else 寫程式碼?試試 “策略模式” 吧!模式
- LRU快取替換策略及C#實現快取C#
- 記一次使用策略模式優化程式碼的經歷模式優化
- 過多if - else 的問題, 以及策略模式 + 反射解決方法模式反射
- bash 引數替換中的模式匹配模式
- 結構型:策略模式模式
- 委派模式與策略模式記錄模式
- 【解決方案】專案重構之如何使用 MySQL 替換原來的 MongoDBMySqlMongoDB
- Spring 實現策略模式--自定義註解方式解耦if...elseSpring模式解耦
- webpack入門筆記——熱替換Web筆記
- 設計模式例項講解 - 里氏替換設計模式
- 如何修改docker容器的重啟策略(重啟模式)?Docker模式
- 實際業務中使用策略模式對程式碼進行重構模式
- HTML 替換元素與非替換元素HTML
- 專案中的if else太多了,該怎麼重構?
- Find and Replace Pattern(C++查詢和替換模式)C++模式
- 記一次百萬行WPF專案程式碼的重構記錄
- 【LeetCode】424. 替換後的最長重複字元LeetCode字元
- Vi替換
- 替換空格
- 嘻哈說:設計模式之里氏替換原則設計模式
- js replace替換字串,同時替換多個方法JS字串
- 重構 - 觀察者模式模式
- 替換橫槓
- linux文字替換Linux
- SQL Server 替換SQLServer
- 替換燈桶
- 2018-09-06 替換樂視sdk記錄
- MRAM獨特功能替換現有記憶體記憶體