我们在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. 报错:require_once cannot allocate memory----php,以前自己弄的稍微有点特殊的开发环境

    最近出现过一个问题,值得记录 类似于这样的报错的问题: Warning: require_once(/www/app/somecomponent.php): failed to open stream ...

  2. Centos 7服务器搭建MySQL(mariadb)服务

    1.下载并安装MySQL yum install mariadb mariadb-server -y 2.启动MySQL systemctl start mariadb 3.对mariadb进行初始化 ...

  3. PHP open_basedir配置未包含upload_tmp_dir 导致服务器不能上传文件

    在做一个上传图片的功能时候发现后台接收到的$_FILES['file']['error'] = 6,这个错误意思是找不到临时文件,或者是临时文件夹无权限,需要更改php.ini文件的 upload_t ...

  4. python学习04数据

    #1.**幂 //返回商的整数部分x=5y=3print(x**y)print(x//y)print(5/2)#2.复数a+bjc=2+5jprint(c.real)#返回复数的实部print(c.i ...

  5. 2019-2020-1 20199329《Linux内核原理与分析》第四周作业

    <Linux内核原理与分析>第四周作业 一.上周问题总结: 虚拟机环境缺少部分库文件 书本知识使用不够熟练 二.本周学习内容: 1.实验楼环境使用gdb跟踪调试内核 1.1 在该环境下输入 ...

  6. Ubuntu 设置 log 级别

    Linux环境下使用rsyslog管理日志 rsyslog linux运维 linux 22.7k 次阅读  ·  读完需要 22 分钟     在 Linux 系统中,日志文件记录了系统中包括内核. ...

  7. 初入React源码(一)

    导语 React是我接触的第二个框架,我最初开始接触的是vue,但是并没有深入的理解过vue,然后在工作过程中,我开始使用了React,现在已经觉得React会比vue更加实用,但是这只是个人观点,可 ...

  8. HTML(css 样式)

    1.CSS 可以通过以下方式添加到 HTML 中: 内联样式 -- 在 HTML 元素中使用 "style" 属性 内部样式表 -- 在 HTML 文档头部 <head> ...

  9. Circle of Monsters(贪心)

    n个怪物围成一圈,每个怪物有自己的血量和爆炸伤害. 怪物在死后会对下一个怪物造成爆炸伤害,又死了又可以爆炸...... 你每发子弹可以对怪物造成1点伤害,求杀死所有怪物的最小子弹数. 传送门 \(\c ...

  10. python学习笔记-零碎知识点

    1. 绝对值 abs(-4) 结果: 4 2.