設計模式之策略模式
一、策略模式定義:就是定義一組演算法,每種演算法都封裝起來,并且使他們之間可以相互替換,
策略模式使用的是面向物件的繼承和多型的機制
策略模式中三個角色:Context封裝角色、Strategy抽象策略角色、ConcreteStrategy具體策略角色
* Context 封裝角色:背景關系角色,主要就是起到封裝的作用,屏蔽高層模塊對演算法、策略的直接訪問,封裝可能存在的變化
* Strategy抽象策略角色:策略、演算法家族的抽象,
* ConcreteStrategy :實作抽象策略中的操作,該類具有具體的演算法
二、策略模式類圖:

通用原始碼格式,寫起來也很簡單
a.抽象的策略角色:
public interface Strategy { // 策略模式的遠演算法則 public void doSomething(); }View Code
b.具體的策略角色
// 具體實作1@Slf4j public class ConcreteStrategy1 implements Strategy { @Override public void doSomething() { log.info("策略1的實作"); } } // 具體實作2 @Slf4j public class ConcreteStrategy2 implements Strategy { @Override public void doSomething() { log.info("策略2的實作"); } }View Code
c.封裝角色
public class Context { // 抽象策略 private Strategy strategy = null; // 建構式設定具體策略 public Context(Strategy strategy) { this.strategy = strategy; } // 執行 public void exec() { strategy.doSomething(); } }View Code
d.高層實作
public class Client { public static void main(String[] args) { //1.宣告一個具體的策略 Strategy strategy = new ConcreteStrategy1(); //2.宣告一個背景關系物件 Context context = new Context(strategy); // 執行封裝后的方法 context.exec(); } }View Code
三、策略模式實作案例一:
注解+反射 的方式實作策略模式
從北京前往河南的出行方式
1.設計一個抽象策略類
public interface TravelStrategy { /** * 出行方式 */ void tripMode(); }View Code
2.創建綠皮火車、高鐵、飛機三種出行方式,不同經濟身份的人,做不同的工具進行出行
@Slf4j @Component @Strategy(value = "green_trip") public class GreenTrip implements TravelStrategy{ @Override public void tripMode() { log.info("綠皮火車的出行方式"); } }View Code
@Slf4j @Component @Strategy(value="high_speed_trip") public class HighSpeedRailTrip implements TravelStrategy { @Override public void tripMode() { log.info("高鐵的出行方式"); } }View Code
@Slf4j @Component @Strategy(value = "airplane_trip") public class AirplaneTrip implements TravelStrategy { @Override public void tripMode() { log.info("飛機的出行方式"); } }View Code
3.創建一個策略注解
@Target(value =https://www.cnblogs.com/yingxiaocao/p/ ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Strategy { String value(); }View Code
4.創建一個獲取物件的工具類
@Component public class BeanUtils implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { BeanUtils.applicationContext = applicationContext; } public static Object getBean(Class clazz) { return applicationContext.getBean(clazz); } }View Code
5.創建一個策略工廠
public class StrategyFactory { private static volatile StrategyFactory instance; private StrategyFactory() { } public static StrategyFactory getInstance() { if (instance == null) { synchronized (StrategyFactory.class) { if (instance == null) { instance = new StrategyFactory(); } } } return instance; } private static final String TRIP_STRATEGY_PACKAGE = "com.xxx.strategy"; private static final HashMap<String, Class> strategyMap = new HashMap<>(); static { Reflections reflections = new Reflections(TRIP_STRATEGY_PACKAGE); Set<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(Strategy.class); typesAnnotatedWith.forEach(aClass -> { Strategy annotation = aClass.getAnnotation(Strategy.class); strategyMap.put(annotation.value(), aClass); }); } /** * 根據固定的key,獲取固定的策略 * * @param type * @return */ public static TravelStrategy getStrategy(String type) { Class aClass = strategyMap.get(type); if (aClass == null) { return null; } return (TravelStrategy) BeanUtils.getBean(aClass); } }View Code
6.創建一個controller
@RequestMapping("/travel")
@RestController
public class TravelController {
@RequestMapping("/trip")
public void trip(@RequestParam String person) {
TravelStrategy strategy = StrategyFactory.getStrategy(TripModeEnum.getValue(person));
strategy.tripMode();
}
}
View Code
7.最后創建一個列舉類
public enum TripModeEnum { /** * 綠皮火車 */ GREENTRAIN("1", "green_trip"), HIGH_SPEED_RAIL_TRIP("2", "high_speed_trip"), AIRPHONE("3", "airplane_trip"); private String personType; private String strategy; private TripModeEnum(String personType, String strategy) { this.personType = personType; this.strategy = strategy; } public String getPersonType() { return personType; } public void setPersonType(String personType) { this.personType = personType; } public String getStrategy() { return strategy; } public void setStrategy(String strategy) { this.strategy = strategy; } public static String getValue(String key) { TripModeEnum[] values = TripModeEnum.values(); for (TripModeEnum modeEnum : values) { if (modeEnum.getPersonType().equals(key)) { return modeEnum.getStrategy(); } } return null; } }View Code
這種是傳統的策略模式實作方式,當時有個缺點就是類會增多,每一個策略都是一個類
策略模式實作案例二
利用Map與函式式介面來實作
同樣是出行方式的案例
這里使用的是函式式介面,當然也可以自己寫的普通的介面,實際業務情況,可能需要自定義,這里使用的BiConsume,我比較懶,直接用現成的介面
1.創建一個出行策略類
@Slf4j @Service public class TripModeStrategy { @Autowired private TripService tripService; /** * 策略map */ private Map<String, BiConsumer<String, String>> strategyMap = new HashMap<>(); @PostConstruct public void initMap() { strategyMap.put(TripDictConstant.GREEN_TRAIN, (person, sex) -> tripService.greenTrip(person, sex)); strategyMap.put(TripDictConstant.HIGH_SPEED_RAIL_TRIP, (person, sex) -> tripService.highSpeedRail(person, sex)); strategyMap.put(TripDictConstant.AIRPHONE, (person, sex) -> tripService.airphone(person, sex)); } /** * 出行方式實作類 * * @param key key * @param person person * @param sex sex */ public void tripMode(String key, String person, String sex) { BiConsumer<String, String> tripStrategy = strategyMap.get(key); if (tripStrategy == null) { log.info("沒有找到出行策略"); return; } tripStrategy.accept(person, sex); } }View Code
2.創建一個具體的策略實作類
@Slf4j @Service public class TripService { /** * 綠皮火車出行方式 */ public void greenTrip(String person, String sex) { log.info("姓名" + person); log.info("性別" + sex); log.info("坐車綠皮火車環繞地球了"); } /** * 高鐵出行方式 */ public void highSpeedRail(String person, String sex) { log.info("姓名" + person); log.info("性別" + sex); log.info("坐車高鐵,,,,,,"); } /** * 飛機的出行方式 */ public void airphone(String person, String sex) { log.info("姓名" + person); log.info("性別" + sex); log.info("坐車飛機,,,飛升了"); } }View Code
3.創建一個資料字典類
public class TripDictConstant { /** * 綠皮火車的出行方式 */ public static final String GREEN_TRAIN = "1"; /** * 高鐵的出行方式 */ public static final String HIGH_SPEED_RAIL_TRIP = "2"; /** * 飛機的出行方式 */ public static final String AIRPHONE = "3"; }View Code
4.創建controller介面類
@RequestMapping("/travel")
@RestController
public class TravelController {
@Autowired
private TripModeStrategy tripModeStrategy;
@RequestMapping("/trip")
public void trip(@RequestParam String key,@RequestParam String person,@RequestParam String sex) {
tripModeStrategy.tripMode(key,person,sex);
}
}
View Code
呼叫postman,執行結果,結果土撥鼠,坐車小飛機,不可思議的飛升了


使用這種方式,實作策略模式,類的使用明顯少了,也更加的直觀
策略模式實作案例三:
列舉值使用策略模式
1.創建一個列舉值策略類
@Slf4j public enum TripEnumStrategy { /** * 綠皮火車 */ GREENTRAIN("1", "green_train") { @Override public void tripMode(String person, String sex) { log.info("姓名" + person); log.info("性別" + sex); log.info("坐車綠皮火車環繞地球了"); } }, HIGH_SPEED_RAIL_TRIP("2", "high_speed_trip") { @Override public void tripMode(String person, String sex) { log.info("姓名" + person); log.info("性別" + sex); log.info("坐車高鐵火車環繞地球了"); } }, AIRPHONE("3", "airplane_trip") { @Override public void tripMode(String person, String sex) { log.info("姓名" + person); log.info("性別" + sex); log.info("坐者飛機,,,上天了"); } }; private String personType; private String strategy; private TripEnumStrategy(String personType, String strategy) { this.personType = personType; this.strategy = strategy; } public String getPersonType() { return personType; } public void setPersonType(String personType) { this.personType = personType; } public String getStrategy() { return strategy; } public void setStrategy(String strategy) { this.strategy = strategy; } public static String getValue(String key) { TripEnumStrategy[] values = TripEnumStrategy.values(); for (TripEnumStrategy modeEnum : values) { if (modeEnum.getPersonType().equals(key)) { return modeEnum.name(); } } return null; } public abstract void tripMode(String personType, String sex); }View Code
2.創建一個controller
@RequestMapping("/travel")
@RestController
public class TripController {
@RequestMapping("/trip")
public void trip(@RequestParam String key, @RequestParam String person, @RequestParam String sex) {
String stragegy = TripEnumStrategy.getValue(key);
TripEnumStrategy.valueOf(stragegy).tripMode(person, sex);
}
}
View Code
運行執行結果

根據型別實作了策略之間的相互轉換
四、策略模式的優缺點
優點:
1.演算法之間可以自由切換
2.避免多重條件判斷
如果不適用策略模式,如果一個介面功能,業務型別有20多種,一會兒要用A策略,一會兒要用B策略,使用多重if陳述句,多重條件不容易維護,而且代碼寫的臃腫不堪,可讀性極差,出錯概率極大,
想想都是恐怖,我在公司看著別人寫的代碼,幾千行的方法,嵌套個不停,我真是心累的很,深有體會
3.擴展性良好
體現在新增加一個策略,只要實作介面就可以了,其他的都不用修改,類似于一個可反復拆卸的插件
缺點:
1.策略類數量較多
2.所有的策略類都需要對外暴露
五、策略類的使用場景
1.多個類只有在演算法或者行為上稍有不同的場景
2.演算法需要自由切換的場景
演算法的選擇是使用者來決定的,或者演算法始終在進化,特別是一些技術前沿的行業,連業務規則都無法告訴你這樣的業務規則能用多長時間,在這種情況下使用策略模式
3.需要屏蔽演算法規則的場景
太多的演算法,只需要記住一個名字就好了,傳遞想關的資料key,回傳一個結果,就好了
注意:如果一個策略類家族中具體的策略數量超過4個,就要考慮解決類膨脹、和對外暴露的問題,要不然日后維護的時候,就難受成皮皮蝦了
誒!專案組的成員,架構師寫的代碼真心很優秀,羨慕,一般的開發寫的代碼,真是臃腫,如果不是可讀性、可維護性、可擴展性太差,讓我接手的時候,難受不已,我可能也不會重新學習設計模式,我可能也就是完成功能實作,不報錯,就完事了,真是被逼的,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/4543.html
標籤:設計模式
上一篇:設計模式(3) 抽象工廠模式
下一篇:設計模式(4) 建造者模式
