当前位置:首页 > 科技  > 软件

接手了个项目,被if..else搞懵逼了

来源: 责编: 时间:2024-01-15 09:21:05 300观看
导读背景领导:“这个项目,今后就给你维护了啊,仔细点。” 小猫:“好,没问题”。 可当满怀信心的小猫打开项目工程包翻看一些代码之后,瞬间懵逼没了信心。是这样的:还是这样的:平级的if else密密麻麻就算了,但是深套五六层的if else

背景

领导:“这个项目,今后就给你维护了啊,仔细点。” 小猫:“好,没问题”。 可当满怀信心的小猫打开项目工程包翻看一些代码之后,瞬间懵逼没了信心。m4c28资讯网——每日最新资讯28at.com

m4c28资讯网——每日最新资讯28at.com

是这样的:m4c28资讯网——每日最新资讯28at.com

m4c28资讯网——每日最新资讯28at.com

还是这样的:m4c28资讯网——每日最新资讯28at.com

m4c28资讯网——每日最新资讯28at.com

平级的if else密密麻麻就算了,但是深套五六层的if else甚至七八层的真的是让人摸不着北。m4c28资讯网——每日最新资讯28at.com

开启优化

那么就上面小猫遇到的这种情况,面对着几代程序员精心堆积的屎山,试问阁下该如何应对?不慌,老猫罗列了以下解决方案,如果各位还有比较好的优化方法也欢迎留言。m4c28资讯网——每日最新资讯28at.com

m4c28资讯网——每日最新资讯28at.com

我们对着上述目录从简单的开始介绍吧:m4c28资讯网——每日最新资讯28at.com

1.提前return法

当我们遇到空对象或者有部分满足条件之后才能执行的时候,不要只想着正向逻辑,其实可以逆向思维,把不满足条件的优先排除掉。这样可以有效避免if else的深嵌套。 优化前代码:m4c28资讯网——每日最新资讯28at.com

if(condition){//doSomething}else{}return;

优化后如下:m4c28资讯网——每日最新资讯28at.com

if(!condition){  return;}

2.能省则省,规避最后的else

原来的代码:m4c28资讯网——每日最新资讯28at.com

public Result addUser() { if (StrUtil.equals(userStatus, "online")) {     return doStep1(); } else {  return doStep2(); } // else 后面没有其他业务时,可省略最后的else,使代码简洁}

优化后的代码:m4c28资讯网——每日最新资讯28at.com

public Result addUser() { if (StrUtil.equals(userStatus, "online")) {      return doStep1(); }  return doStep2();}

当然这里面要注意的点是,一定要确认是最后的else,并没有其他的业务逻辑。m4c28资讯网——每日最新资讯28at.com

3.三目运算符

还是基于上面的代码,如果只有两种业务的话,其实在一个方法里面直接用三目运算法进行执行即可。如下改造:m4c28资讯网——每日最新资讯28at.com

public Result addUser() {  return StrUtil.equals(userStatus, "online")) ?doStep1() : doStep2();}

一个方法一行代码搞定。m4c28资讯网——每日最新资讯28at.com

4.使用optional

很多业务场景下,其实我们写if 是为了判空,自从java8之后其实多了一个Optional神器,Optional 是个容器,它可以保存类型 T 的值,或者仅仅保存null。Optional 提供了很多方法,这样我们就不用显式进行空值检测。Optional 类的引入很好的解决空指针异常。我们看下下面的优化方式: 代码优化前:m4c28资讯网——每日最新资讯28at.com

if (user == null) {    throw new Exception("未查询到用户信息");}if (user != null) {    update(user); // 执行方法调用}

代码优化后:m4c28资讯网——每日最新资讯28at.com

Optional.ofNullable(user).orElseThrow(() -> new Exception("未查询到用户信息"));Optional.ofNullable(user).ifPresent(user -> update(user));

隐式调用相当优雅。m4c28资讯网——每日最新资讯28at.com

5.设计模式优化法

设计模式优化法其实也是针对不同的场景使用不同的设计模式从而简化多余的if else。m4c28资讯网——每日最新资讯28at.com

(1) 第一种,合理使用责任链模式。m4c28资讯网——每日最新资讯28at.com

我们再具体结合一种场景,比方说现在页面上有新注册的用户,他需要提交相关的身份信息进行认证,此时,我们底层往往会对他提交的信息做相关的校验处理。 底层我们的校验方式(1)需要验证基本字非空性 (2)需要验证身份信息基础字段合法性 (2)需要调用第三方进行要素认证。 原始代码如下:m4c28资讯网——每日最新资讯28at.com

public void addUser(User user) { // 1.非空校验 if (StrUtil.isBlank(user.getUsername())) {  throw new RuntimeException("用户名为空!"); } if (StrUtil.isBlank(user.getPassword())) {  throw new RuntimeException("密码为空!"); } ...  // 2.格式校验 if (!ValidUtil.isIdCardNo(user.getIdCardNo())) {  throw new RuntimeException("身份证号格式错误!"); } if (!ValidUtil.isEmail(user.getEmail())) {  throw new RuntimeException("手机号格式错误!"); } if (!ValidUtil.isEmail(user.getEmail())) {   throw new RuntimeException("邮箱格式错误!"); }    ... // 3.要四素认证校验  if(!doFourStampVerify(User user)){   throw new RuntimeException("四要素认证失败!");  }}

此处可能还有很多其他的省略的场景。所以单个文件中的If else可能比想象中多的多。那么我们如何用责任链模式进行优化呢? 改造代码如下,首先定义一个处理器接口:m4c28资讯网——每日最新资讯28at.com

/** * 处理器链接口 */public interface UserChainHandler {    void handler(User user);}

剩下不同的场景校验只要去实现这个接口就可以了,不过需要定义好顺序m4c28资讯网——每日最新资讯28at.com

@Component@Order(1) // 指定注入顺序public class UserParamNullValidChainHandler implements UserChainHandler {    @Override    public void handler(User user) {     // 1.非空校验     if (StrUtil.isBlank(user.getUsername())) {   throw new RuntimeException("用户名为空!");  }  if (StrUtil.isBlank(user.getPassword())) {   throw new RuntimeException("密码为空!");  }}@Component@Order(1) // 指定注入顺序public class UserParamNullValidChainHandler implements UserChainHandler {    @Override    public void handler(User user) {     // 1.非空校验     if (StrUtil.isBlank(user.getUsername())) {   throw new RuntimeException("用户名为空!");  }    ...}/** * 格式校验处理器 */@Component@Order(2) // 指定注入顺序public class UserParamFormatValidChainHandler implements UserChainHandler {     @Override    public void handler(User user) {     // 2.格式校验  if (!ValidUtil.isIdCardNo(user.getIdCardNo())) {   throw new RuntimeException("身份证号格式错误!");  }    ...}/** * 四要素处理器 */@Component@Order(3) // 指定注入顺序public class FourElementVerifyChainHandler implements UserChainHandler {     @Override    public void handler(User user) {     // 2.格式校验  if (!doFourStampVerify(User user)) {   throw new RuntimeException("四要素认证失败!");  }}
//进行组装@Component@RequiredArgsConstructorpublic class UserChainContext {        private final List<UserChainHandler> userChainHandlerList; // 自动注入责任链处理器        /**     * 责任链组件执行     *     * @param requestParam 请求参数     */    public void handler(User user) {        // 此处根据 Ordered 实际值进行排序处理        userChainHandlerList.forEach(x -> x.handler(user));    }}

最终咱们的原来的add方法进行这样调用就好了:m4c28资讯网——每日最新资讯28at.com

public void addUser(User user) { // 执行责任链 userChainContext.handler(user);}

(2) 第二种,合理使用策略模式+工厂模式。m4c28资讯网——每日最新资讯28at.com

假设我们遇到这样一个场景,我们目前底层是一个会员系统,目前系统需要计算各种会员套餐的价格,然后套餐的具体模式主要是由上层系统传递指定给我们。如果只关注业务直接撸代码的话,应该是如下。m4c28资讯网——每日最新资讯28at.com

public Result calcPrice(CalcPriceParam calcPriceParam){  //判断对应的计算价格的场景  Integer type = judgeType(calcPriceParam);  //根据场景调用不同的方法 ,建议更好的编码习惯是把type改成枚举类型哈~  if(type == 1){    return calcPriceForTypeOne();  }  if(type == 2){    return calcPriceForTypeTwo();  }  if(type == 3){    return calcPriceForTypeThree();  }  .....  if(typr == 10){    return calcPriceForTypeTen();  }}

显而易见随着会员价格场景套餐越来越多,我们的if也会越来越多。 但是如果使用策略模式的话,我们可以做到如下:m4c28资讯网——每日最新资讯28at.com

public interface Strategy {  Result calcPrice(CalcPriceParam calcPriceParam);  int getBizType();}@Servicepublic Class firstStragy implement Strategy {  Result calcPrice(CalcPriceParam calcPriceParam) {    ....    return result;  }  int getBizType() {    return 1;  }}public Class secondStragy implement Strategy {  Result calcPrice(CalcPriceParam calcPriceParam) {    ....    return result;  }  int getBizType() {    return 2;  }}@Servicepublic class StrategyContext{  Map<Integer,CalcPriceInterface> strategyContextMap = new HashMap<>();  //注入对应的策略类  @Autowired  Strategy[] strategys;      @PostConstruct  public void setStrategyContextMap(){    for(Stragegy stragegy:strategys){        strategyContextMap.put(stragegy.getCode,stragegy);    }  }  //根据场景调用不同的方法   public Result calcPrice(CalcPriceParam calcPriceParam){   Integer type = judgeType(calcPriceParam);    CalcPriceInterface calcPriceInstance = strategyContextMap.get(type);    return calcPriceInstance.calcPrice(calcPriceParam);  }}

这样一来,咱们上面的第一个方法中的If else的实现将会变得很简单,如下:m4c28资讯网——每日最新资讯28at.com

@AutowiredStrategyContext strategyContext;public Result calcPrice(CalcPriceParam calcPriceParam){  strategyContext.calcPrice(calcPriceParam);}

这样即使新增新的计算模式,我们只需去实现Strategy接口并且重写里面两个方法即可完成后续业务的拓展。代码优雅简单,可维护性强。 以上就是用设计模式针对大量if else进行改造。m4c28资讯网——每日最新资讯28at.com

6.表驱动法

这种方式个人觉得有点像策略模式,但是又不需要单独抽出相关类去承载注册方法,而是简单地将方法通过函数式的方式放到Map中,等到需要使用的时候再进行调用。 原始烂代码,我们还是参考上述会员费用金额计算的场景。我们可以进行如下方式优化:m4c28资讯网——每日最新资讯28at.com

Map<String, Function<?> action> actionMap = new HashMap<>();action.put("type1",() -> {calcPriceForTypeOne()});action.put("type2",() -> {calcPriceForTypeTwo()});action.put("type3",() -> {calcPriceForTypeThree()});...// 使用actionMap.get(action).apply();

当然如果想要再优化得好一些的话,可以进行接口抽取,然后进行实现,在此不展开,留下给小伙伴们思考一下。m4c28资讯网——每日最新资讯28at.com

7.其他场景灵活运用,干掉if else

我们再回到之前小猫遇到的那两个代码截图,其实我们可以看到有个大量if else并排的代码其实主要是想要比较相关的属性有没有发生变化,如果发生变化,那么则返回false,没有变化则返回true。其实我们想想是不是可以通过重写LogisticDO这个对象的equals方法来进行实现呢?这样是不是也规避了大量的if else。m4c28资讯网——每日最新资讯28at.com

还有其他一些当然也是根据具体场景来解决,比方说,我需要根据不同的type类型,进行获取不同的描述信息,那么此时我们是不是可以使用enum去维护呢? 如下:m4c28资讯网——每日最新资讯28at.com

if(status.equals(1)){   return "订单未支付";}else if(status.equals(2)){   return "订单已支付"}else if(status.equals(3)){   return "订单已发货"}.....

优化后:m4c28资讯网——每日最新资讯28at.com

@Getter@AllArgsConstructorpublic enum OrderStatusEnum {    UN_PAID("1","订单未支付"),    PAIDED("2","订单已支付"),    SENDED("3","订单已发货"),    .....;    private String status;    private String statusDes;    static OrderStatusEnum of(String status) {        for (OrderStatusEnum statusEnum : OrderStatusEnum.values()) {            if (statusEnum.getStatus().equals(status)) {                return statusEnum;            }        }        return null;    }}String orderStatusDes = OrderStatusEnum.of(orderStatus).getStatusDes();

等等还有其他一些,由于这些优化个人认为是没法标准化的优化原则,不同的业务场景都不同,所以在此,老猫不将其放在通用优化中,认为这个是其他优化方式。m4c28资讯网——每日最新资讯28at.com

结束语

之前在某个技术论坛上看到大家在争论这么一个问题“如何避免将维护的项目发展成屎山?”大家发言踊跃。有说前期做好设计,有人说代码质量需要高一些,合理场景套用一些设计模式等等。 不过老猫认为项目无法避免发展成屎山,只是快慢而已,我也认为项目无法避免发展成“屎山”。其原因有三点:m4c28资讯网——每日最新资讯28at.com

  • 项目代码维护者经过好几轮,每次开发技术水平参差不齐,代码风格也不同。
  • 项目迭代中途有很多突发状况,比方说为了解决Hotfix临时上线,为了赶项目临时上线,大家为了赶工完成业务需求,代码质量可能就可想而知了。
  • 虽然经过好几轮研发之手,有的研发害怕改出业务问题,所以选择继续堆屎山。

说了这么多,其实老猫最终想表达的是,虽然项目会最终沦为屎山,但是作为一个有追求的研发,我们就应当从每个小的if else着手,至少让当前这个项目在你维护期间,让其发展成屎山的速度变慢一些,或者能替之前的老前辈还掉一些技术债才是最好的,各位小伙伴你们觉得呢?m4c28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-60958-0.html接手了个项目,被if..else搞懵逼了

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 终究还是拿下字节!强度拉满!

下一篇: Java 新技术:虚拟线程使用指南

标签:
  • 热门焦点
  • 鸿蒙OS 4.0公测机型公布:甚至连nova6都支持

    华为全新的HarmonyOS 4.0操作系统将于今天下午正式登场,官方在发布会之前也已经正式给出了可升级的机型产品,这意味着这些机型会率先支持升级享用。这次的HarmonyOS 4.0支持
  • 不容错过的MSBuild技巧,必备用法详解和实践指南

    一、MSBuild简介MSBuild是一种基于XML的构建引擎,用于在.NET Framework和.NET Core应用程序中自动化构建过程。它是Visual Studio的构建引擎,可在命令行或其他构建工具中使用
  • Java NIO内存映射文件:提高文件读写效率的优秀实践!

    Java的NIO库提供了内存映射文件的支持,它可以将文件映射到内存中,从而可以更快地读取和写入文件数据。本文将对Java内存映射文件进行详细的介绍和演示。内存映射文件概述内存
  • 虚拟键盘 API 的妙用

    你是否在遇到过这样的问题:移动设备上有一个固定元素,当激活虚拟键盘时,该元素被隐藏在了键盘下方?多年来,这一直是 Web 上的默认行为,在本文中,我们将探讨这个问题、为什么会发生
  • 每天一道面试题-CPU伪共享

    前言:了不起:又到了每天一到面试题的时候了!学弟,最近学习的怎么样啊 了不起学弟:最近学习的还不错,每天都在学习,每天都在进步! 了不起:那你最近学习的什么呢? 了不起学弟:最近在学习C
  • 新电商三兄弟,“抖快红”成团!

    来源:价值研究所作 者:Hernanderz 随着内容电商的概念兴起,抖音、快手、小红书组成的&ldquo;新电商三兄弟&rdquo;成为业内一股不可忽视的势力,给阿里、京东、拼多多带去了巨大压
  • 阿里大调整

    来源:产品刘有媒体报道称,近期淘宝天猫集团启动了近年来最大的人力制度改革,涉及员工绩效、层级体系等多个核心事项,目前已形成一个初步的&ldquo;征求意见版&rdquo;:1、取消P序列
  • 华为HarmonyOS 4.0将于8月4日发布 或搭载AI大模型技术

    华为宣布HarmonyOS4.0将于8月4日正式发布。此前,华为已经针对开发者公布了HarmonyOS4.0,以便于开发者提前进行适配,也因此被曝光出了一些新系统的特性
  • 中关村论坛11月25日开幕,15位诺奖级大咖将发表演讲

    11月18日,记者从2022中关村论坛新闻发布会上获悉,中关村论坛将于11月25至30日在京举行。本届中关村论坛由科学技术部、国家发展改革委、工业和信息化部、国务
Top