借助 Spring 实现策略模式
策略模式:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。
应用场景:
- 计算商品优惠的时候会有各种优惠券,满减券、奖金券、折扣券等
- 抽奖过程中会使用到多种不同的抽奖算法
合理地使用策略模式可以让代码更具有可维护性,替代了大量的 if-else 语句。
策略模式通常和模板模式结合在一起使用,模板模式用于定义算法骨架,而策略模式则用于定义算法中的可变部分。
策略模式总结起来有以下几个部分:
- 抽象策略类(Strategy):策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。
- 具体策略类(ConcreteStrategy):实现抽象策略中的操作,含有具体的算法。
- 环境类(Context):持有一个策略类的引用,最终给客户端调用。
如何借助 Spring 实现策略模式?
- 定义抽象策略类(Strategy):
1public interface Strategy implements InitializingBean{2 /**3 * 策略方法4 */5 void doSomething();6
7
8 /**9 * 设置策略key10 */11 int getStrategyKey();12
13 @Override14 default void afterPropertiesSet() throws Exception {15 int key = getStrategyKey();3 collapsed lines
16 Context.registerStrategy(key, this);17 }18}
- 定义具体策略类(ConcreteStrategy):
1public class ConcreteStrategyA implements Strategy {2 @Override3 public void doSomething() {4 System.out.println("具体策略A的实现");5 }6
7 @Override8 public int getStrategyKey() {9 return 1;10 }11}
1public class ConcreteStrategyB implements Strategy {2 @Override3 public void doSomething() {4 System.out.println("具体策略B的实现");5 }6
7 @Override8 public int getStrategyKey() {9 return 2;10 }11}
- 定义环境类(Context):
1public class Context {2 private Map<Integer, Strategy> strategyMap = new HashMap<>();3
4 public static registerStrategy(int key, Strategy strategy) {5 strategyMap.put(key, strategy);6 }7
8 public void doAnything(int key) {9 Strategy strategy = strategyMap.get(key);10 strategy.doSomething();11 }12}
- 测试
1public class Test {2 public static void main(String[] args) {3 Context context = new Context();4 context.doAnything(1);5
6 context = new Context(new ConcreteStrategyB());7 context.doAnything(2);8 }9}
InitializingBean
InitializingBean的作用是Bean注入到Spring容器且初始化后,执行特定业务化的操作。
Spring允许容器中的Bean,在Bean初始化完成后或者Bean销毁前,执行特定业务化的操作,常用的实现方式有以下三种:
- 通过实现InitializingBean/DisposableBean接口来处理初始化后/销毁前的操作;
- 通过标签的init-method/destroy-method属性处理初始化后/销毁前的操作;
- 在指定方法上加上@PostConstruct或@PreDestroy注解来处理初始化后/销毁前的操作
如果采用实现InitializingBean接口的方式去执行特定业务化的操作,则需要重写afterPropertiesSet这个方法
优化
我们还可以基于注解的方式来标注具体的策略类,结合PostConstruct注解,我们可以将注解标注在具体的策略类上,这样我们就可以在初始化后执行特定业务化的操作。
1@Target({ElementType.TYPE})2@Retention(RetentionPolicy.RUNTIME)3public @interface StrategyAnnoation {4 Constants.StrategyMode strategyMode();5}
1 public enum StrategyMode {2
3 SINGLE(1, "策略A"),4
5 ENTIRETY(2, "策略B");6
7 private Integer code;8 private String info;9
10 StrategyMode(Integer code, String info) {11 this.code = code;12 this.info = info;13 }14
15 ...getter/setter1 collapsed line
16 }
1public class Context {2
3 @Resource4 private List<Strategy> strategyList;5
6 protected static Map<Integer, Strategy> strategyGroup = new ConcurrentHashMap<>();7
8 @PostConstruct9 public void init() {10 strategyList.forEach(strategy -> {11 Strategy strategy = AnnotationUtils.findAnnotation(strategy.getClass(), StrategyAnnoation.class);12 if (null != strategy) {13 strategyGroup.put(strategy.strategyMode().getCode(), strategy);14 }15 });3 collapsed lines
16 }17
18}