我们在web开发中,经常使用数据库表中的字段作为“标记”来表示多个“状态”,比如:

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

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

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

select * from orders where zt = #{zt}

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

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

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

常规方式如何解决?

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

    这个很容易的,在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进行优化(这里就不写代码了,因为和上边并没有任何区别)。

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

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

思考如何干掉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”,获取更多的学习内容

不吹牛X,我真的干掉了if-else的更多相关文章

  1. 【Android进阶】Android面试题目整理与讲解(一)

    这一篇文章专门整理一下研究过的Android面试题,内容会随着学习不断的增加,如果答案有错误,希望大家可以指正 1.简述Activity的生命周期 当Activity开始启动的时候,首先调用onCre ...

  2. 大数据学习(7)Hadoop高可用

    HDFS高可用 通过主从切换实现单NameNode高可用.通过Federation:水平扩展来联合多NameNode个: NameNode高可用 把edits日志从原来的nameNode中分离出来,存 ...

  3. 两年Java的面试经验

    前言:从过年前就萌生出要跳槽的想法,到过年来公司从3月初提出离职到23号正式离职,上班的时间也出去面试过几家公司,后来总觉的在职找工作总是得请假,便决心离职后找工作.到4月10号找到了一家互联网公司成 ...

  4. Android面试题目整理与解说(一)

    这一篇文章专门整理一下研究过的Android面试题,内容会随着学习不断的添加,假设答案有错误,希望大家能够指正 1.简述Activity的生命周期 当Activity開始启动的时候,首先调用onCre ...

  5. 干掉Session?这个跨域认证解决方案真的优雅!

    用户登录认证是 Web 应用中非常常见的一个业务,一般的流程是这样的: 客户端向服务器端发送用户名和密码 服务器端验证通过后,在当前会话(session)中保存相关数据,比如说登录时间.登录 IP 等 ...

  6. 程序bug致损失400亿,判程序员坐牢? 搞笑我们是认真的

    号外!号外!走过,路过,不要错过!日本 IT 业的狗血八卦继续独家放送啦!! 2015 年 9 月 3 日,随着东京最高法院驳回瑞穗证券的上诉,维持二审的原判结果,一个长达 10 年的诉讼终于画下了句 ...

  7. 不要听吹牛逼什么前端MVVM框架就是好,其实都是一帮没学好分层设计的搞出来的,让你彻底看清前端MVVM的本质

    最近前端圈子里面,发现大家都在热炒概念,什么knockout,angularJs,都被捧成神了,鄙人不才,最近心情也不好,特地写这篇文章来找骂 写代码的码农都知道,Java社区虽然不是一个提出分层思想 ...

  8. [置顶] 请听一个故事------>你真的认为iPhone只是一部手机?苹果惊天秘密!!

    在网上看到的一篇小说,感觉有点意思,转载过来大家一起围观下,作者很幽默很风趣. 导读:iPhone的隐藏功能!Jobs的军方身份!图灵服毒自杀的传奇故事!中兴华为的神秘背景! 你真的认为iPhone只 ...

  9. 你真的懂ajax吗?

    前言 总括: 本文讲解了ajax的历史,工作原理以及优缺点,对XMLHttpRequest对象进行了详细的讲解,并使用原生js实现了一个ajax对象以方便日常开始使用. damonare的ajax库: ...

随机推荐

  1. 数值计算方法实验之Lagrange 多项式插值 (Python 代码)

    一.实验目的 在已知f(x),x∈[a,b]的表达式,但函数值不便计算,或不知f(x),x∈[a,b]而又需要给出其在[a,b]上的值时,按插值原则f(xi)= yi(i= 0,1…….,n)求出简单 ...

  2. pytorch 手写数字识别项目 增量式训练

    dataset.py ''' 准备数据集 ''' import torch from torch.utils.data import DataLoader from torchvision.datas ...

  3. 用long类型让我出了次生产事故,写代码还是要小心点

    昨天发现线上试跑期的一个程序挂了,平时都跑的好好的,查了下日志是因为昨天运营跑了一家美妆top级淘品牌店,会员量近千万,一下子就把128G的内存给爆了,当时并行跑了二个任务,没辙先速写一段代码限流,后 ...

  4. phpspider框架的使用

    手册:https://doc.phpspider.org/configs-members.html 参考:https://www.jianshu.com/p/01052508ea7c 不多说,代码贴上 ...

  5. &和&&,|和||的用法

    表达式一$a && $b ,表达式二$a & $b 1.相同点:两个表达式都是当$a.$b都为true时,表达式为真.两种运算符对此表达式结果没有影响. 2.不同点:表达式$a ...

  6. An example shows several CIM-XML extension headers

    The example below shows several CIM-XML extension headers for a GetClass operation on the root/cimv2 ...

  7. js 运动函数篇 (一) (匀速运动、缓冲运动、多物体运动、多物体不同值运动、多物体多值运动)层层深入

    前言:         本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽.         本篇文章为您分析一下原生JS写 匀速运动.缓冲运动.多物体运 ...

  8. 【java基础】01 计算机基础知识

    一.计算机基础知识 1. 计算机 1. 什么是计算机? 计算机在生活中的应用举例 计算机(Computer)全称:电子计算机,俗称电脑.是一种能够按照程序运行,自动.高速处理海量数据的现代化智能电子设 ...

  9. 【Linux题目】第四关

    1. 如何过滤出已知当前目录下oldboy中的所有一级目录? 提示:不包含oldboy目录下面目录的子目录和隐藏目录,只要一级目录即可. 解答: ls -F|grep /   通过ls -F给目录后面 ...

  10. 打造livecd的注意事项

    一:在CentOS.ks的定制脚本中,删除syslinux组件:出错提示: /usr/lib/python2.6/site-packages/imgcreate/errors.py:45: Depre ...