不吹牛X,我真的干掉了if-else

咱们在web开发中,常用数据库表中的字段做为“标记”来表示多个“状态”,好比:前端

咱们就以某宝的在线购物流程为例进行分析。在订单表中,使用zt字段来表示定单的状态,常见的状态就有:java

状态码 状态说明
0 待付款
1 待发货
2 待收货
3 待评价
4 售后

当咱们想按条件查询各个类型的订单的时候,只须要一个接口,在前端传入相应的状态码就能够了。在dao层大概也就是经过以下的语句进行查询:git

select * from orders where zt = #{zt}
复制代码

如何才能有很高的扩展性?

假设有这么几个“不成需求的需求”:web

  1. 我想让待收货的订单按照订单发货时间或者预计送达时间排序,其余的暂且按照订单建立时间排序吧
  2. 想将“待收货”的状态区分开,分为“用户未收到货”和“用户收到货可是未点击确认收货按纽”两种状态

常规方式如何解决?

  • ​ 需求一(不一样的状态处理方式不一样):sql

    这个很容易的,在sevice层添加一个判断就能够,其余的代码不用改,代码以下:数据库

// 2 表示待收货
if(zt == 2){
  //按照需求,按照订单发货时间或者预计送达时间排序
}else{
  //其余状态的订单,所有按照订单建立时间排序
}
复制代码

​ 上边这个代码的修改量已经很小了,可是若是我要把和种不一样的状态订单所有按照不一样的排序方式排序呢?你可能会写以下代码后端

if(zt==0){
  // 待付款的订单处理代码...
}else if(zt==1){
  
}else if(zt==2){
  
}else if(zt==3){
  
}else if(zt==4){
  
}
复制代码

上边代码太low了,有些小伙伴可能会使用switch进行优化(这里就不写代码了,由于和上边并无任何区别)。微信

  • 需求二(添加一个新的状态表示):app

    这也很easy啊,直接在上边的if-else或者switch代码中添加新的状态判断不就行了。ide

思考如何干掉if-else?

上边的方式能够完成咱们的需求,可是有如下几点不足:

1. 面对“各类各样奇怪的需求”,咱们要频繁地修改上边的代码,时间久了,岂不成了渣渣。甚至咱们本身都不肯意再去看这些代码了;
2. 若是新增长一个状态表示,也就是给zt字段新的状态含义表示,咱们又要添加if-else,这太复杂了。
复制代码

使用策略模式来解决if-else的问题

是的,就是使用策略模式来解决进行太多的状态判断代码就是一个好办法。好比,就上边每个if-else中的代码抽成一个类或者方法进行处理。

主要的代码我就不写了,由于下边才是咱们的主菜,这里说的这种方式只能解决if-else里边的代码复杂问题,将代码进行必定程度上的解耦。但并无实质地解决if-else的问题,并且这也是网上大多数的解决办法。

若是对策略模式不太了解的小伙伴,能够看下这篇文章,不看也不要紧,在下边你会看到怎么用的。策略模式的学习之道

尝试使用Spring来配合策略模式

程序设计的一大原则“对扩展开放,对修改关闭”,定义一个接口类,用来查询不一样状态的订单列表。以下:

public interface OrderService {

    /** * 查询对应状态的订单列表 * @param zt * @return */
    List<Order> getOrderList(String zt);
}
复制代码

而后根据不一样的订单状态建立不一样的实现类,好比,“待付款”的订单查询类以下:

虽然如下的命名方式属于错误示范,可是却能很好地理解

@Service("orderServiceDfk") // 这个命名确实很不友好,可是我相信你能理解哈
public class PendingParymentOrderSeviceImpl implements OrderService {
    /** * 查询待付款的订单列表 * * @param zt * @return */
    @Override
    public List<Order> getOrderList(String zt) {
      //这里要利用dao层从数据库中查询出来相应的订单列表
      return null;
    }
复制代码

看了这两个类的代码,我相信小伙伴们应该能理解了要怎么作了,就是根据前端传来不一样的zt值,后台使用不一样的类来处理,可是咱们能够经过Spring来彻底取掉if-else。

咱们的controller层代码以下:

@RestController
public class OrderController {
  private String orderServiceBeanNamePrefix = "orderService";

  @RequestMapping("getOrderList/{zt}")
  public List<Order> getOrderList(@PathVariable("zt") String zt) {

    //获取对应的处理状态的bean来处理
    //就经过这样一句代码,彻底解决了if-else的判断逻辑
    OrderService orderService = (OrderService) SpingContext.getBean(orderServiceBeanNamePrefix + zt);
    List<Order> orderList = orderService.getOrderList();

    return orderList;
  }
}
复制代码

上边用了一个工具类,就是从Spring 容器中获取相应的bean,代码以下:

/** * 微信公众号 “小鱼与Java” * * 原理很简单,咱们写的类实现这个接口,具体能够查阅Spring生命周期相关内容 * Spring会自动调用其中的setApplicationContext方法,传入Spring容器上下文 * 咱们就在这里把Spring上下文保存下来 * * @date 2020/5/18 * @auther Lyn4ever */
@Component
public class SpingContext implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    /** * 根据name从Spring容器中获取bean * @param name * @return */
    public static Object getBean(String name){
        return applicationContext.getBean(name);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("我保存了Spring上下文");
        applicationContext = applicationContext;
    }
}
复制代码

总结:

解决if-else的思路就是使用策略模式,针对不一样“状态”的订单,使用不一样的类来处理逻辑,这样就能够很好地进行了“解耦”操做。可是,若是新增一个“状态表示 ”,咱们就要在主逻辑处添加if-else进行判断要用哪一个类来处理。

而解决这个“判断 ”的中使用的if-else就有不少方法:抽象工厂也是一个不错的方法。而咱们使用Spring的控制反转一样也能够很好地解决这个问题。这么作的好处以下:

  1. “真正的”解决了与咱们业务无关的if-else;
  2. 不用先后端再进行状态的表示“约定”,以前用0表示“待付款”,1表示 “待发货”这样的操做,若是记错,那必定会有大问题。如今,使用特定的字符串来表示,也就是说,前端直接传入想要解决这个方案对应的bean,从而少去了“复杂且易出错的约定”环节。

代码地址:

以为不错的,能够关注个人微信公众号哦

关注微信公众号“小鱼与Java”,获取更多的学习内容

相关文章
相关标签/搜索