使用模板方法改善 if else , 增加可维护性

以财务录入ECS等订单到系统为例.

以前😣

  1. public void handle(OrderContext orderContext){
  2. if (新购) {
  3. } else if (续费) {
  4. } else if (升级) {
  5. } else if (弹性升级) {
  6. } else if (退款){
  7. }else {
  8. //...
  9. }
  10. }

一开始还挺简洁明了的,慢慢的就开始了

财务A: 这个账号的订单不要录入了. 是私人账号
财务B: 能不能吧其他平台的订单也支持一下.
采购: …..

慢慢的,慢慢的,一个体型膨大的 if else 就开始缓缓的成长起来了. 到最后就没人敢动了:)
image.png

现在😯

使用模板方法改善的前提是这些操作存在相似性,但是又稍微有点区别,也可以用 if else 去搞定.

订单分类🚪

  • 新购
  • 续费
  • 升级
  • 退款
  • 等等

建立模板 😂

  1. public abstract class BaseOrder {
  2. //处理订单,每个类型都是不一样的 抽象处理啊
  3. public abstract void handleOrder(OrderContext orderContext);
  4. //是否支持这个订单类型
  5. public boolean support(String orderType) {
  6. final Fuck fuck = this.getClass().getAnnotation(Fuck.class);
  7. return Arrays.stream(fuck.value()).collect(Collectors.toSet()).contains(orderType);
  8. }
  9. //获取订单类型
  10. public abstract Fuck getSupportedOrderType();
  11. }

一个具体的例子 🐎

  1. @Component
  2. @Fuck({"New"})
  3. public class NewOrder extends BaseOrder {
  4. @Override
  5. public void handleOrder(OrderContext orderContext) {
  6. System.out.println("==>NewOrder");
  7. }
  8. @Override
  9. public Fuck getSupportedOrderType() {
  10. return this.getClass().getAnnotation(Fuck.class);
  11. }
  12. }

处理模板 🌲

我们肯定是不希望每次新增一个模板都要改动很多地方. 最后是直接写一个类继承 BaseOrder 加上 Fuck 的注解就可以了

处理订单

订单接口😌

  1. public class OrderContext {
  2. private Object order;
  3. private String orderType;
  4. //...get set
  5. }
  6. public interface OrderHandlerService {
  7. void handle(OrderContext orderContext);
  8. }

接口实现😄

  1. @Component
  2. public class OrderHandler implements OrderHandlerService, InitializingBean, BeanFactoryAware {
  3. private final List<BaseOrder> orderList = new ArrayList<>();
  4. private BeanFactory beanFactory;
  5. @Override
  6. public void handle(OrderContext orderContext) {
  7. //从orderList找到一个能处理该订单的实例,并处理,如果没有则抛出异常
  8. final BaseOrder baseOrder = orderList.stream()
  9. .filter(x -> x.support(orderContext.getOrderType()))
  10. .findAny()
  11. .orElseThrow(() -> new OrderRejectException("不支持的订单类型"));
  12. baseOrder.handleOrder(orderContext);
  13. }
  14. @Override
  15. public void afterPropertiesSet() throws Exception {
  16. Assert.notNull(beanFactory, "beanFactory can't be null");
  17. //找到所有的带有Fuck注解的实现类,填充到orderList中
  18. final String[] beanNames = BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors((ListableBeanFactory) beanFactory, Fuck.class);
  19. for (String beanName : beanNames) {
  20. orderList.add((BaseOrder) beanFactory.getBean(beanName));
  21. }
  22. }
  23. @Override
  24. public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
  25. this.beanFactory = beanFactory;
  26. }
  27. }

修改以后,更加美妙了.
要支持多平台,简单
要支持其他的设置,简单
更多的订单类型,简单.

思考🤔📒

如果有翻看过一些开源项目的源代码的话就会觉得这个处理方式好像在哪里见过😂,例如在Spring/mybatis中就出现的比较多, 一个简洁的描述

  • 一个接口
    • 两个抽象
    • 四个实现
    • 如果还不满足,可以自定义实现.