设计模式之策略模式
设计模式之策略模式
一、策略模式定义:就是定义一组算法,每种算法都封装起来,并且使他们之间可以相互替换。
策略模式使用的是面向对象的继承和多态的机制
策略模式中三个角色:Context封装角色、Strategy抽象策略角色、ConcreteStrategy具体策略角色
* Context 封装角色:上下文角色,主要就是起到封装的作用。屏蔽高层模块对算法、策略的直接访问,封装可能存在的变化
* Strategy抽象策略角色:策略、算法家族的抽象。
* ConcreteStrategy :实现抽象策略中的操作,该类具有具体的算法
二、策略模式类图:

通用源码格式,写起来也很简单
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(); } }
三、策略模式实现案例一:
注解+反射 的方式实现策略模式
从北京前往河南的出行方式
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);
}
}
运行执行结果

根据类型实现了策略之间的相互转换
四、策略模式的优缺点
优点:
1.算法之间可以自由切换
2.避免多重条件判断
如果不适用策略模式,如果一个接口功能,业务类型有20多种,一会儿要用A策略,一会儿要用B策略。使用多重if语句,多重条件不容易维护,而且代码写的臃肿不堪,可读性极差,出错概率极大。
想想都是恐怖,我在公司看着别人写的代码,几千行的方法,嵌套个不停,我真是心累的很,深有体会
3.扩展性良好
体现在新增加一个策略,只要实现接口就可以了。其他的都不用修改,类似于一个可反复拆卸的插件
缺点:
1.策略类数量较多
2.所有的策略类都需要对外暴露
五、策略类的使用场景
1.多个类只有在算法或者行为上稍有不同的场景
2.算法需要自由切换的场景
算法的选择是使用者来决定的。或者算法始终在进化。特别是一些技术前沿的行业。连业务规则都无法告诉你这样的业务规则能用多长时间。在这种情况下使用策略模式
3.需要屏蔽算法规则的场景
太多的算法,只需要记住一个名字就好了,传递想关的数据key,返回一个结果,就好了
注意:如果一个策略类家族中具体的策略数量超过4个,就要考虑解决类膨胀、和对外暴露的问题。要不然日后维护的时候,就难受成皮皮虾了
诶!项目组的成员,架构师写的代码真心很优秀,羡慕。一般的开发写的代码,真是臃肿,如果不是可读性、可维护性、可扩展性太差,让我接手的时候,难受不已,我可能也不会重新学习设计模式,我可能也就是完成功能实现,不报错,就完事了。真是被逼的。

浙公网安备 33010602011771号