我们在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. 高德局部刷新标记点,bug解决

    将接口返回的经纬集合点在高德地图上标记展示, 如果实时刷新地图标记点,不加优化,则会造成过多的带宽消耗 所以,地图只需加载一次,局部更新标记点就好了 代码: <template> < ...

  2. MySQL中出现Unknow column 'xx' in field list的解决办法

    首先创建一个表,然后插入数据发现出错误 经过多次尝试发现title前面多了一个空格 我们把空格去掉,然后在插入数据,发现数据创建成功

  3. java学习(第二篇)语法学习

    1.java标识符 类名.变量名以及方法名都被称为标识符. 关于 Java 标识符,有以下几点需要注意: 所有的标识符都应该以字母(A-Z 或者 a-z),美元符($).或者下划线(_)开始 首字符之 ...

  4. 如何在 Inno Setup 中执行命令行的命令

    Pascal Scripting: Exec Prototype: function Exec(const Filename, Params, WorkingDir: String; const Sh ...

  5. Copy ArrayList的四种方式

    目录 简介 使用构造函数 使用addAll方法 使用Collections.copy 使用stream 总结 Copy ArrayList的四种方式 简介 ArrayList是我们经常会用到的集合类, ...

  6. 在Spring Boot中配置web app

    文章目录 添加依赖 配置端口 配置Context Path 配置错误页面 在程序中停止Spring Boot 配置日志级别 注册Servlet 切换嵌套服务器 在Spring Boot中配置web a ...

  7. OSChina 清明节乱弹 ——准备好纸巾了看乱弹

    2019独角兽企业重金招聘Python工程师标准>>> Osc乱弹歌单(2017)请戳(这里) [今日歌曲] @亚麻仔 :分享 范忆堂 的歌曲<酡颜 (夏热版)> 同一个 ...

  8. web前端项目中遇到的一些问题总结(08.23更新)

    个人网站 https://iiter.cn 程序员导航站 开业啦,欢迎各位观众姥爷赏脸参观,如有意见或建议希望能够不吝赐教! 写一些最近工作中Vue项目中遇到的问题. 巴啦啦小魔仙,污卡拉,全身变,小 ...

  9. 《Android的设计与实现:卷I》——第1章 1.2.2动态视角的体系结构

    1.2.2 动态视角的体系结构静态的体系结构是从横向分层的角度诠释Android是什么.如果静态的体系结构不足以让读者理解Android的运行机制,我们可以看看Google工程师Sans Serif是 ...

  10. 有关for循环的一些东西

    有的时候,不知道是因为学的有点浅显,还是脑袋有点懵,简单的循环语句都有点被绕糊涂了. 这种内外循环的,先是外循环一次,内循环全部,接着再外循环第二次,内循环全部,,,,,,,. 所以先是显示 0 4 ...