从放弃到入门-策略模式

391 阅读2分钟

阿里巴巴开发者手册上只提到了超过三层的if-else的逻辑判断代码可以使用策略模式来实现,但是并没有告诉你怎么实现。下面介绍一下常规的实现方式。

实现方式

需求背景

在做订单时效判定时,要根据订单的类型做不同的操作,目前主要包括四种类型:

  1. 全推即时单,对应枚举对象NORMAL_CANCELORDER
  2. 全推预订单,对应枚举对象BOOK_CANCELORDER
  3. 选推即时单,对应枚举对象XUANTUI_CANCELORDER
  4. 选推预订单,对应枚举对象BOOK_XUANTUI_CANCELORDER

一、定义策略接口

public enum  TimerJudgeBizTypeEnum {

    NORMAL_CANCELORDER(1, "全推即时单"),
    BOOK_CANCELORDER(2, "BOOK_CANCELORDER"),
    XUANTUI_CANCELORDER(3, "选推即时单"),
    BOOK_XUANTUI_CANCELORDER(4, "选推预订单");

    private int code;

    private String desc;

    TimerJudgeBizTypeEnum(int code, String desc){
        this.code = code;
        this.desc = desc;
    }

}
public interface TimerJudgeService {

    boolean isMatched(TimerJudgeBizTypeEnum bizTypeEnum);

    Response<T> getTimerJudge(Long orderId);

}

定义了策略的接口,包括两个方法:

  • isMatched()用于获取匹配的策略类型;
  • getTimerJudge()用于处理具体的策略逻辑

二、具体的策略实现类

全推即时单的业务逻辑处理:

@Service
public class NormalCancelOrderJudgeTimerHandler implements TimerJudgeService {

    @Override
    public boolean isMatched(TimerJudgeBizTypeEnum bizTypeEnum) {

        return TimerJudgeBizTypeEnum.NORMAL_CANCELORDER.equals(bizTypeEnum);

    }

    @Override
    public Response<T> getTimerJudge(Long orderId) {
        System.out.println("全推即时单执行业务逻辑ing。。。。");
        return Response.success("返回结果");
    }
}

全推预订单的业务逻辑处理:

@Service
public class BookCancelOrderJudgeTimerHandler implements TimerJudgeService {

    @Override
    public boolean isMatched(TimerJudgeBizTypeEnum bizTypeEnum) {

        return TimerJudgeBizTypeEnum.BOOK_CANCELORDER.equals(bizTypeEnum);

    }

    @Override
    public Response<T> getTimerJudge(Long orderId) {
        System.out.println("全推预订单执行业务逻辑ing。。。。");
        return Response.success("返回结果");
    }
}

其他两个场景类似

三、建立manager处理类

@Service
public class TimerJudgeManager implements ApplicationContextAware, InitializingBean {

    private ApplicationContext applicationContext;

    private List<TimerJudgeService> handlers = new ArrayList<>();
    
     public TimerJudgeService doGetHandle(TimerJudgeBizTypeEnum bizTypeEnum) {
        for (TimerJudgeService handler : handlers) {
            if (handler.isMatched(bizTypeEnum)) {
                return handler;
            }
        }
        return null;
    }

    @Override
    public void afterPropertiesSet() {

        for (TimerJudgeService handle : applicationContext.getBeansOfType(TimerJudgeService.class).values()) {
            handlers.add(handle);
        }

    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

四、 测试类

@Service
public class TimerJudgeTest {

    @Autowired
    private TimerJudgeManager timerJudgeManager;

    public Response<T> test(TimerJudgeBizTypeEnum bizTypeEnum) {
        TimerJudgeService timerJudgeService = doGetHandle(bizTypeEnum);
        System.out.println("返回结果" + timerJudgeService);
    }
}

可以发现,通过让TimerJudgeManager实现ApplicationContextAware和InitializingBean两个接口,然后在afterPropertiesSet方法中就可以基于Spring容器启动时自动注册所有的策略实现类到handlers这个hashmap容器中。然后在使用过程中根据不同的业务场景直接doGetHandle方法来获得具体的策略实现类。
综上,TimerJudgeManager只负责获取对应的handler实现类,各个handler实现类只负责实现各自的业务逻辑,达到了“高内聚低耦合”的效果。并且,当需要新增策略类时,只需要实现接口即可。

优缺点

优点

  • 避免了大量的if-else语句导致的耦合性太高、代码比较臃肿、维护代价很高;
  • 遵循开闭原则,实现代码的解耦合。当新增一种策略场景时,仅仅新增一个实现类即可,扩展性非常好,而且每个实现类符合单一职责原则。

缺点:

  • 要求客户端必须知道所有的策略类,然后再调用的时候传入对应的策略类型;

使用场景

  • 对客户端隐藏具体策略实现细节,彼此完全独立;
  • 多个实现类只是业务细节不同,在运行时要动态选择具体要执行的类;