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

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

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

背景

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

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

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

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

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

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

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

开启优化

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

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

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

1.提前return法

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

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

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

if(!condition){  return;}

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

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

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

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

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

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

3.三目运算符

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

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

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

4.使用optional

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

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

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

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

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

5.设计模式优化法

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

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

我们再具体结合一种场景,比方说现在页面上有新注册的用户,他需要提交相关的身份信息进行认证,此时,我们底层往往会对他提交的信息做相关的校验处理。 底层我们的校验方式(1)需要验证基本字非空性 (2)需要验证身份信息基础字段合法性 (2)需要调用第三方进行要素认证。 原始代码如下:kbe28资讯网——每日最新资讯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可能比想象中多的多。那么我们如何用责任链模式进行优化呢? 改造代码如下,首先定义一个处理器接口:kbe28资讯网——每日最新资讯28at.com

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

剩下不同的场景校验只要去实现这个接口就可以了,不过需要定义好顺序kbe28资讯网——每日最新资讯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方法进行这样调用就好了:kbe28资讯网——每日最新资讯28at.com

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

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

假设我们遇到这样一个场景,我们目前底层是一个会员系统,目前系统需要计算各种会员套餐的价格,然后套餐的具体模式主要是由上层系统传递指定给我们。如果只关注业务直接撸代码的话,应该是如下。kbe28资讯网——每日最新资讯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也会越来越多。 但是如果使用策略模式的话,我们可以做到如下:kbe28资讯网——每日最新资讯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的实现将会变得很简单,如下:kbe28资讯网——每日最新资讯28at.com

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

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

6.表驱动法

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

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

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

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

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

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

优化后:kbe28资讯网——每日最新资讯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();

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

结束语

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

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

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

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

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

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

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

标签:
  • 热门焦点
  • 官方承诺:K60至尊版将会首批升级MIUI 15

    官方承诺:K60至尊版将会首批升级MIUI 15

    全新的MIUI 15今天也有了消息,在官宣了K60至尊版将会搭载天玑9200+处理器和独显芯片X7的同时,Redmi给出了官方承诺,K60至尊重大更新首批升级,会首批推送MIUI 15。也就是说虽然
  • 红魔电竞平板评测:大屏幕硬实力

    红魔电竞平板评测:大屏幕硬实力

    前言:三年的疫情因为要上网课的原因激活了平板市场,如今网课的时代已经过去,大家的生活都恢复到了正轨,这也就意味着,真正考验平板电脑生存的环境来了。也就是面对着这种残酷的
  • 一文搞定Java NIO,以及各种奇葩流

    一文搞定Java NIO,以及各种奇葩流

    大家好,我是哪吒。很多朋友问我,如何才能学好IO流,对各种流的概念,云里雾里的,不求甚解。用到的时候,现百度,功能虽然实现了,但是为什么用这个?不知道。更别说效率问题了~下次再遇到,
  • 最“俊美”淘宝卖家,靠直播和短视频圈粉,上架秒光,年销3000万

    最“俊美”淘宝卖家,靠直播和短视频圈粉,上架秒光,年销3000万

    来源 | 电商在线文|易琬玉编辑|斯问受访店铺:Ringdoll戒之人形图源:微博@御座的黄山、&ldquo;Ringdoll戒之人形&rdquo;淘宝店铺有关外貌的评价,黄山已经听累了。生于1985年的他,哪
  • 共享单车的故事讲到哪了?

    共享单车的故事讲到哪了?

    来源丨海克财经与共享充电宝相差不多,共享单车已很久没有被国内热点新闻关照到了。除了一再涨价和用户直呼用不起了。近日多家媒体再发报道称,成都、天津、郑州等地多个共享单
  • 大厂卷向扁平化

    大厂卷向扁平化

    来源:新熵作者丨南枝 编辑丨月见大厂职级不香了。俗话说,兵无常势,水无常形,互联网企业调整职级体系并不稀奇。7月13日,淘宝天猫集团启动了近年来最大的人力制度改革,目前已形成一
  • 网红炒股不为了赚钱,那就是耍流氓!

    网红炒股不为了赚钱,那就是耍流氓!

    来源:首席商业评论6月26日高调宣布入市,网络名嘴大v胡锡进居然进军了股市。在一次财经媒体峰会上,几个财经圈媒体大佬就&ldquo;胡锡进炒股是否知道认真报道&rdquo;展开讨论。有
  • 东方甄选单飞:有些鸟注定是关不住的

    东方甄选单飞:有些鸟注定是关不住的

    作者:彭宽鸿来源:华尔街科技眼&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;东方甄选创始人俞敏洪带队的&ldquo;7天甘肃行&rdquo;直播活动已在近日顺利收官。成立后一
  • 信通院:小米、华为等11家应用商店基本完成APP签名及验签工作

    信通院:小米、华为等11家应用商店基本完成APP签名及验签工作

    中国信通院表示,目前,小米、华为、OPPO、vivo、360手机助手、百度手机助手、应用宝、豌豆荚和努比亚等9家应用商店,以及抖音和快手2家新型应用分发平
Top