我们在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. 掌握游戏开发中类Message、Handle

    1.   实验目的 1. 自主地设计图形界面 2. 掌握消息类Message的应用 3. 掌握消息处理类Handle的应用 4. 掌握子线程中中更新UI界面的方法 2.  实验内容 1. 在主界面设置 ...

  2. Java中Random类

    Random:产生随机数的类 构造方法: public Random();没有给种子,用的是默认种子,是当前时间的毫秒值. public Random(long seed);给出指定的种子 //给定种 ...

  3. Scala学习系列(三)——入门与基础

    本课程源码共享于 https://github.com/tree1123/learning-scala 首先,打开IDEA编辑器的SbtExampleProject项目,我们将在这个项目下进行练习 本 ...

  4. js输入框练习

    这个就是一个输入框的小练习(也是第一次写这个东西) <!DOCTYPE html> <html lang="en"> <head> <me ...

  5. 百度paddlepaddle学习体会

    一个偶然从微信公众号中刷到了<python小白逆袭A1大神>的文章,让我不经意的邂逅了飞桨(paddlepaddle),通过加入飞桨训练营一周的学习.实践,对飞桨有了很多的了解(飞桨官网: ...

  6. Docker安装yapi

    安装docker 1.安装依赖包: yum install -y yum-utils device-mapper-persistent-data lvm2 2.安装 Yum -y install do ...

  7. 创建线程的方式三:实现Callable接口-----JDK5.0 新增

    package com.yhqtv.java2; /* * 创建线程的方式三:实现Callable接口-----JDK5.0 新增 * * 如何理解实现Callable接口的方式创建多线程比实现Run ...

  8. Gartner:2016 Q2全球服务器市场,中国再度成为亮点

    对于IT产品供应商来说,Gartner.IDC等第三方分析机构市场调研报告无疑是一次"中考",成绩优异的论功行赏,迎接鲜花和掌声:差强人意,批斗会将是不可避免的,接下来加班加点,力 ...

  9. Python开源框架总结

    Django: Python Web应用开发框架 Django 应该是最出名的Python框架,GAE甚至Erlang都有框架受它影响.Django是走大而全的方向,它最出名的是其全自动化的管理后台: ...

  10. nginx经验分享

    如果我们在使用启动nginx时,遇到这样的提示: nginx: [alert] could not open error log file: open() "/usr/local/var/l ...