06-10-設計模式 策略模式
阿新 • • 發佈:2022-05-27
策略模式
基本介紹
1)策略模式(StrategyPattern)中,定義演算法族(策略組),分別封裝起來,讓他們之間可以互相替換,此模式讓演算法的變化獨立於使用演算法的客戶
2)這演算法體現了幾個設計原則,第一、把變化的程式碼從不變的程式碼中分離出來;第二、針對介面程式設計而不是具體類(定義了策略介面);第三、多用組合/聚合,少用繼承(客戶通過組合方式使用策略)。
說明:從上圖可以看到,客戶context有成員變數strategy或者其他的策略介面,至於需要使用到哪個策略,我們可以在構造器中指定
策略模式優化IF else
類圖
使用策略模式+工廠+模板方法 解決多分類查詢
- 瀏覽器呼叫Controller, Controller呼叫Service, Service呼叫查詢工廠
- 在WarningQueryFactory中維護查詢Key和具體查詢的的關係, 並實現ApplicationContextAware介面獲取到IOC, 然後通過IOC獲取抽象查詢類呼叫(AbstractWarningQuery, 並呼叫抽象查詢類的模板方法
- 在WaringQuery介面中定義統一查詢方法
- 使用AbstractWaringQuery對其進行實現, 並在其中定義模板方法, 並且在模板方法中呼叫抽象介面
- 具體查詢實現類繼承抽象查詢類, 並實現WarningQuery介面中的查詢方法, 同時註冊到IOC中, 可以讓工廠從IOC中獲取到
這是一個標準的策略+模板的實現. 本來我想在其中加入狀態模式, 用於控制是根據一些引數, 來決定查詢DB還是查詢快取, 但是後來應為一些場景是快取實現不了的, 只能查DB了, 但是一些公用資料還是查詢快取的, 後續如果需要擴充套件其他查詢, 只需要在工廠層中維護對映關係, 並新增新的實現類繼承抽象查詢類(AbstractWariningQuery), 並實現WarningQuery介面的查詢方法即可
程式碼實現
Controller層
@GetMapping("/queryWarningByKeyPage") public Object queryWarningByKey(@Valid WarningQueryParam warningQueryParam) { return warningService.queryWarningByKey(warningQueryParam); }
Service層
/** * 根據配置決定查詢資料庫還是快取預設為資料庫,快取還有問題(2022/5/12 已修復) * * @param warningQueryParam 預警查詢條件 * @return 資料 */ public Object queryWarningByKey(WarningQueryParam warningQueryParam) { String key = warningQueryParam.getKey(); String deptName = warningQueryParam.getDeptName(); Long pageNum = warningQueryParam.getPageNum(); Long pageSize = warningQueryParam.getPageSize(); return warningQueryFactory.queryDataByKey(key, deptName, pageNum, pageSize); }
工廠
@Component public class WarningQueryFactory implements ApplicationContextAware { public static final ConcurrentHashMap<String, String> context = new ConcurrentHashMap<>(); private ApplicationContext applicationContext; static { // context.put("021","opwWarningQuery"); context.put("022","opwWarningQuery"); String videoWarning = "videoWarningQuery"; context.put("011",videoWarning); } public Object queryDataByKey(String key,String deptName,Long pageNum, Long pageSize){ if("021".equals(key)){ throw new BusinessException("該型別介面暫未實現, 敬請期待!"); } String s = context.get(key); AbstractWaringQuery bean = applicationContext.getBean(s, AbstractWaringQuery.class); return bean.queryWarningByKeyPageBase(key,deptName,pageNum,pageSize); } @Override public void setApplicationContext(@Nonnull ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
高層查詢介面
public interface WaringQuery { Object queryWaringByKeyPage(String key,String deptName, Long pageNum, Long pageSize); }
抽象實現者
public abstract class AbstractWaringQuery implements WaringQuery { public Object queryWarningByKeyPageBase(String key,String deptName,Long pageNum, Long pageSize){ if(pageNum == null || pageNum < 1){ pageNum = 1L; } if(pageSize == null || pageSize < 1){ pageSize = 4L; } return queryWaringByKeyPage(key,deptName,pageNum,pageSize); } }
具體實現者OPW
@Component public class OpwWarningQuery extends AbstractWaringQuery { @Autowired private RedisUtil redisUtil; @Autowired private DeptService deptService; @Autowired private PressureService pressureService; @Value("${query.warning.type}") private String queryType; @Autowired private UserContextService userContextService; @Autowired private OnlineService onlineService; @Override public CommonPage<List<PressureVo>> queryWaringByKeyPage(String key, String deptName, Long pageNum, Long pageSize) { // 暫時不支援Redis了 // if (QueryType.REDIS.equals(queryType)) { // return queryDataByRedis(key, pageNum, pageSize); // } return queryDateByDb(key,deptName, pageNum, pageSize); } private CommonPage<List<PressureVo>> queryDateByDb(String key,String deptName, Long pageNum, Long pageSize) { // 業務邏輯 } private CommonPage<List<PressureVo>> queryDataByRedis(String key, Long pageNum, Long pageSize) { // 業務邏輯 } private List<PressureVo> addIsOnline(List<Pressure> records, List<String> deptCodes) { // 業務邏輯 } }
策略模式的注意事項和細節
1)策略模式的關鍵是:分析專案中變化部分與不變部分
2)策略模式的核心思想是:多用組合/聚合少用繼承;用行為類組合,而不是行為的繼承。更有彈性
3)體現了“對修改關閉,對擴充套件開放”原則,客戶端增加行為不用修改原有程式碼,只要新增一種策略(或者行為)即可,避免了使用多重轉移語句(if..elseif..else)
4)提供了可以替換繼承關係的辦法:策略模式將演算法封裝在獨立的Strategy類中使得你可以獨立於其Context改變它,使它易於切換、易於理解、易於擴充套件
5)需要注意的是:每新增一個策略就要增加一個類,當策略過多是會導致類數目龐