設計模式之策略模式算法
1、策略模式定義:就是定義一組算法,每種算法都封裝起來,而且使他們之間能夠相互替換。設計模式
策略模式使用的是面向對象的繼承和多態的機制架構
策略模式中三個角色:Context封裝角色、Strategy抽象策略角色、ConcreteStrategy具體策略角色app
* Context 封裝角色:上下文角色,主要就是起到封裝的做用。屏蔽高層模塊對算法、策略的直接訪問,封裝可能存在的變化ide
* Strategy抽象策略角色:策略、算法家族的抽象。函數
* ConcreteStrategy :實現抽象策略中的操做,該類具備具體的算法工具
2、策略模式類圖:post
通用源碼格式,寫起來也很簡單學習
this
public interface Strategy { // 策略模式的遠算法則 public void doSomething(); }
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的實現"); } }
c.封裝角色
public class Context { // 抽象策略 private Strategy strategy = null; // 構造函數設置具體策略 public Context(Strategy strategy) { this.strategy = strategy; } // 執行 public void exec() { strategy.doSomething(); } }
d.高層實現
public class Client { public static void main(String[] args) { //1.聲明一個具體的策略 Strategy strategy = new ConcreteStrategy1(); //2.聲明一個上下文對象 Context context = new Context(strategy); // 執行封裝後的方法 context.exec(); } }
3、策略模式實現案例一:
註解+反射 的方式實現策略模式
從北京前往河南的出行方式
1.設計一個抽象策略類
public interface TravelStrategy { /** * 出行方式 */ void tripMode(); }
2.建立綠皮火車、高鐵、飛機三種出行方式,不一樣經濟身份的人,作不一樣的工具進行出行
@Slf4j @Component @Strategy(value = "green_trip") public class GreenTrip implements TravelStrategy{ @Override public void tripMode() { log.info("綠皮火車的出行方式"); } }
@Slf4j @Component @Strategy(value="high_speed_trip") public class HighSpeedRailTrip implements TravelStrategy { @Override public void tripMode() { log.info("高鐵的出行方式"); } }
@Slf4j @Component @Strategy(value = "airplane_trip") public class AirplaneTrip implements TravelStrategy { @Override public void tripMode() { log.info("飛機的出行方式"); } }
3.建立一個策略註解
@Target(value = ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Strategy { String value(); }
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); } }
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); } }
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(); } }
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; } }
這種是傳統的策略模式實現方式,當時有個缺點就是類會增多。每個策略都是一個類
策略模式實現案例二
利用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); } }
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("坐車飛機。。。飛昇了"); } }
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"; }
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); } }
調用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); }
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); } }
運行執行結果
根據類型實現了策略之間的相互轉換
4、策略模式的優缺點
優勢:
1.算法之間能夠自由切換
2.避免多重條件判斷
若是不適用策略模式,若是一個接口功能,業務類型有20多種,一下子要用A策略,一下子要用B策略。使用多重if語句,多重條件不容易維護,並且代碼寫的臃腫不堪,可讀性極差,出錯機率極大。
想一想都是恐怖,我在公司看着別人寫的代碼,幾千行的方法,嵌套個不停,我真是心累的很,深有體會
3.擴展性良好
體如今新增長一個策略,只要實現接口就能夠了。其餘的都不用修改,相似於一個可反覆拆卸的插件
缺點:
1.策略類數量較多
2.全部的策略類都須要對外暴露
5、策略類的使用場景
1.多個類只有在算法或者行爲上稍有不一樣的場景
2.算法須要自由切換的場景
算法的選擇是使用者來決定的。或者算法始終在進化。特別是一些技術前沿的行業。連業務規則都沒法告訴你這樣的業務規則能用多長時間。在這種狀況下使用策略模式
3.須要屏蔽算法規則的場景
太多的算法,只須要記住一個名字就行了,傳遞想關的數據key,返回一個結果,就行了
注意:若是一個策略類家族中具體的策略數量超過4個,就要考慮解決類膨脹、和對外暴露的問題。要否則往後維護的時候,就難受成皮皮蝦了
誒!項目組的成員,架構師寫的代碼真心很優秀,羨慕。通常的開發寫的代碼,真是臃腫,若是不是可讀性、可維護性、可擴展性太差,讓我接手的時候,難受不已,我可能也不會從新學習設計模式,我可能也就是完成功能實現,不報錯,就完事了。真是被逼的。