不是我吹牛逼,这绝对是去掉 if...else 最佳的文章

news2024/11/9 10:08:48

我相信小伙伴一定看过多篇怎么去掉 if…else 的文章,也知道大家都很有心得,知道多种方法来去掉 if…else ,比如 Option,策略模式等等,但我相信大明哥这篇文章绝对是最全,最完备怎么去掉 if…else 的文章,里面有些方法我相信有小伙伴肯定不知道,我也不卖关子,直接进入主题,如何干掉 if…else。

方法一:提前 return

假如有如下代码:

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

这种代码我们一般采用提前 return 的方式,去掉不必要的 else。

if (!condition){
  return
}

doSomething;

这种方法一般只适合分支结构很简单的 if…else,我们可以提前 return ,把一些不必要的 if…else 去掉。

方法二:枚举

枚举其实也是可以去掉 if…else 的,如下:

String orderStatusDes;
if ("1".equals(orderStatus)) {
    orderStatusDes = "订单未支付";
} else if ("2".equals(orderStatus)) {
    orderStatusDes = "订单已支付";
} else if ("3".equals(orderStatus)) {
    orderStatusDes = "订单已发货";
} else if ("4".equals(orderStatus)) {
    orderStatusDes = "订单已签收";
} else if ("5".equals(orderStatus)) {
    orderStatusDes = "订单已评价";
}

可能有小伙伴说,靠,谁会写这种代码?别这么绝对,大明哥工作这么久了,到现在依然看到有工作 5 、6 年的人写这样的代码。这种类型的代码非常适合枚举来解决。

先定义一个枚举类:

@Getter
@AllArgsConstructor
public enum OrderStatusEnum {
    UN_PAID("1","订单未支付"),
    PAIDED("2","订单已支付"),
    SENDED("3","订单已发货"),
    SINGED("4","订单已签收"),
    EVALUATED("5","订单已评价");

    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();

当然一般在实际项目中,这种处理方式也不是最佳的,最佳的方式应该是在数据库里面有一个码值配置表,然后加载到系统缓存中来,在通过 code 去取值。当然枚举也是一种很好的解决方案。

方案三:Optional 判空

我相信各位小伙伴的项目里面一定存在非空判断,如果为空,则抛出异常或者 return。

Order order = getOrderById(id);
if (order == null) {
    return "-1";
} else {
    return order.getOrderStatus();
}

对于这种代码我们利用 Optional 可以非常优雅地解决。

return Optional.ofNullable(order).map(o -> o.getOrderStatus()).orElse("-1");

这种方式是不是非常优雅,有格调。最后补充一句:

防止 NPE,是程序员的基本修养

方案四:表驱动法

表驱动法,是一种让你可以在表中查找信息,而不必用过多的 if…else 来把他们找出来的方法。如下:

if ("code1".equals(action)) {
    doAction1();
} else if ("code2".equals(action)) {
    doAction2();
} else if ("code3".equals(action)) {
    doAction3();
} else if ("code4".equals(action)) {
    doAction4();
} else if ("code5".equals(action)) {
    doAction5();
}

优化方法如下:

Map<String, Function<?> action> actionMap = new HashMap<>();
action.put("code1",() -> {doAction1()});
action.put("code2",() -> {doAction2()});
action.put("code3",() -> {doAction3()});
action.put("code4",() -> {doAction4()});
action.put("code5",() -> {doAction5()});

// 使用
actionMap.get(action).apply();

其实这种方式也不是很好,因为它会显得代码非常臃肿。一种变形方案是将 doAction() 抽象成类。如下:

//1. 先定义一个 ActionService 接口
public interface ActionService {
    void doAction();
}

//2. 然后定义 5 个实现类
public class ActionService1 implements ActionService{
    public void doAction() {
        //do something
    }
}

//3. 加入表中
Map<String, ActionService> actionMap = new HashMap<>();
action.put("code1",new ActionService1());
action.put("code2",new ActionService2());
action.put("code3",new ActionService3());
action.put("code4",new ActionService4());
action.put("code5",new ActionService5());

//4. 调用
actionMap.get(action).doAction();

这种方式是不是比较优雅些!

方案五:策略模式 + 工厂方法

策略模式 + 工厂方法是解决 if…else 用得非常多的方案,它和上面的表驱动法有点儿类似。使用策略模式 + 工厂方法分为几个步骤,以上面例子为例:

  • 把条件模块抽象为一个公共的接口,策略接口
public interface ActionService {
    void doAction();
}
  • 根据每个逻辑,定义出自己具体的策略实现类,如下:
public class ActionService1 implements ActionService{
    public void doAction() {
        //do something
    }
}

public class ActionService2 implements ActionService{
    public void doAction() {
        //do something
    }
}

// 省略其他策略
  • 工厂类,统一调度,用来管理这些策略,如下:
public class ActionServiceFactory {
    private ActionServiceFactory(){

    }

    private static class SingletonHolder{
        private static ActionServiceFactory instance=new ActionServiceFactory();
    }

    public static ActionServiceFactory getInstance(){
        return SingletonHolder.instance;
    }

    private static final Map<String,ActionService> ACTION_SERVICE_MAP = new HashMap<String, ActionService>();

    static {
        ACTION_SERVICE_MAP.put("action1",new ActionService1());
        ACTION_SERVICE_MAP.put("action2",new ActionService2());
        ACTION_SERVICE_MAP.put("action3",new ActionService3());
        ACTION_SERVICE_MAP.put("action4",new ActionService4());
        ACTION_SERVICE_MAP.put("action5",new ActionService5());
    }

    public static ActionService getActionService(String actionCode) {
        ActionService actionService = ACTION_SERVICE_MAP.get(actionCode);
        if (actionService == null) {
            throw new RuntimeException("非法 actionCode");
        }
        return actionService;
    }

    public void doAction(String actionCode) {
        getActionService(actionCode).doAction();
    }
}

单例模式实现工厂类。

  • 使用
ActionServiceFactory.getInstance().doAction("action1");

这种优化方式也是很优雅的,特别适合分支较多,逻辑较为复杂的代码块,这种方式将分支逻辑与业务代码解耦了,是一种很不错的方案。

方案六:责任链模式

你想不到责任链模式也能优化 if…else 吧。责任链我们可以看做是一个单链表的数据结构,一个对象一个对象地过滤条件,符合的就执行,然后结束,不符合的就传递到下一个节点,如果每个对象都无法处理,一般都有一个最终的节点来统一处理。

我们依然以上面那个例子为例。

  • 定义责任链处理请求节点
public abstract class ActionHandler {

    // 后继节点
    protected ActionHandler successor;

    /**
     * 处理请求
     * @param actionCode
     */
    public void handler(String actionCode) {
        doHandler(actionCode);
    }

    // 设置后继节点
    protected ActionHandler setSuccessor(ActionHandler successor) {
        this.successor = successor;
        return this;
    }

    // 处理请求
    public abstract void doHandler(String actionCode);
}

  • 定义首尾节点,用于一些异常情况的处理
// 首节点,判断 actionCode 是否为空
public class HeadHandler extends ActionHandler{

    @Override
    public void doHandler(String actionCode) {
        if (StringUtils.isBlank(actionCode)) {
            throw new RuntimeException("actionCode 不能为空");
        }

        successor.doHandler(actionCode);
    }
}

// 尾节点,直接抛出异常,因为到了尾节点说明当前 code 没有处理
public class TailHandler extends ActionHandler{

    @Override
    public void doHandler(String actionCode) {
        throw new RuntimeException("当前 code[" + actionCode + "] 没有具体的 Handler 处理");
    }
}

  • 定义各个节点具体的实现节点
public class ActionHandler1 extends ActionHandler{

    @Override
    public void doHandler(String actionCode) {
        if ("action1".equals(actionCode)) {
            doAction1();
        } else {
            // 传递到下一个节点
            successor.doHandler(actionCode);
        }
    }
}

public class ActionHandler2 extends ActionHandler{

    @Override
    public void doHandler(String actionCode) {
        if ("action2".equals(actionCode)) {
            doAction2();
        } else {
            // 传递到下一个节点
            successor.doHandler(actionCode);
        }
    }
}

// 省略其他节点
  • 定义工厂,来构建一条完整的责任链,并负责调度
public class ActionHandlerFactory {
    
    private ActionHandler headHandler;
    
    private ActionHandlerFactory(){
        headHandler = new HeadHandler();
        ActionHandler actionHandler1 = new ActionHandler1();
        ActionHandler actionHandler2 = new ActionHandler2();
        ActionHandler actionHandler3 = new ActionHandler3();
        ActionHandler actionHandler4 = new ActionHandler4();
        ActionHandler actionHandler5 = new ActionHandler5();

        ActionHandler tailHandler = new TailHandler();
        
        // 构建一条完整的责任链
        headHandler.setSuccessor(actionHandler1).setSuccessor(actionHandler2).setSuccessor(actionHandler3).
                setSuccessor(actionHandler4).setSuccessor(actionHandler5).setSuccessor(tailHandler);
    }

    private static class SingletonHolder{
        private static ActionHandlerFactory instance=new ActionHandlerFactory();
    }

    public static ActionHandlerFactory getInstance(){
        return SingletonHolder.instance;
    }
        
    public void doAction(String actionCode) {
        headHandler.doHandler(actionCode);
    }
}
  • 使用
ActionHandlerFactory.getInstance().doAction("action1");

方案七:Function

Function 是 Java 8 中的函数式接口,利用好它我们可以极大地简化我们的代码,例如利用它我们可以轻松去掉我们的 if…else。比如有下面一段代码:

// 抛出异常
if (...) {
  throw new RuntimeException("哎呀,有异常哦...")
}

// if...else 分支
if(...) {
  doSomething1();
} else {
  doSomething2();
}

现在我们利用 Function 来处理上面两段代码

处理抛出异常

  • 定义抛出异常的形式的函数式接口
@FunctionalInterface
public interface ThrowExceptionFunction {

    /**
     * 抛出异常
     * @param message
     */
    void throwMessage(String message);
}

这里只需要有一个这样的函数式接口就行,而且方法也没有返回值,是一个消费型接口。

  • 增加判断工具类
public class ValidateUtils {

    /**
     * 抛出异常
     * @param flag
     * @return
     */
    public static ThrowExceptionFunction isTrue(Boolean flag) {
        return (errorMessage) -> {
            if (flag) {
                throw new RuntimeException(errorMessage);
            }
        };
    }
}

ValidateUtils 类也是非常简单的,如果传入的 flag 为 true,则抛出异常。isTrue() 返回值也是刚刚我们定义的 ThrowExceptionFunction。

  • 使用
ValidateUtils.isTrue(flag).throwMessage("哎呀,有异常哦...");

使用方式是不是非常简单?

处理 if…else 分支

其实使用 Function 来去掉 if…else 分支我认为有点儿偏门,因为它非常依赖我们定义的 Function 函数,比如我们定义的方法只有两个参数,那它就只能处理处理两个分支的,对于三个分支的 if…else 则需要重新定义方法。下面以两个分支为例。

  • 定义函数式接口
@FunctionalInterface
public interface ActionHandler {
    void doActionHandler(ActionService trueActionService,ActionService falseActionService);
}

函数式接口中定义了一个方法,doActionHandler(),它有两个参数,分别为:

  1. trueActionService:为 true 时要进行的操作
  2. falseActionService:为 false 时要进行的操作
  • 定义判断方法

增加一个工具类,用来判断为 true 时执行哪个方法,为 false 时执行哪个方法。

public class ActionHandlerUtils {

    public static ActionHandler isTrue(Boolean flag) {
        return (trueActionService,falseActionService) -> {
            if (flag) {
                trueActionService.doAction();
            } else {
                trueActionService.doAction();
            }
        };
    }
}
  • 使用
ActionHandlerUtils.isTrue(true)
        .doActionHandler(() -> {
            //do true Something
        },() ->{
            //do false Something
    });

总结

大明哥在这里总结了 7 中方式用来解决 if…else 的问题,我相信里面总有一两种方案是你比较满意的,七种方案各有优劣,各自有各自的使用场景,我们需要在实践中不断领悟,在重构中不断进化,总结出适合自己最佳的重构方案。

重构之路,任重而道远,各位其行且珍惜

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1164369.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Git保姆级教学(超详细版)

一、Git概述 Git 是一个免费的、开源的 分布式版本控制系统 &#xff0c;可以快速高效地处理从小型到大型的各种 项目。 Git 易于学习&#xff0c;占地面积小&#xff0c;性能极快。 它具有廉价的本地库&#xff0c;方便的暂存区域和多个工作 流分支等特性。其性能优于 Sub…

开发模型(瀑布、螺旋、scrum) 和 测试模型(V、W)、增量和迭代、敏捷(思想)及敏捷开发 scrum

&#x1f9f8;欢迎来到dream_ready的博客&#xff0c;&#x1f4dc;相信您对这篇博客也感兴趣o (ˉ▽ˉ&#xff1b;) 震惊&#xff01;测试人员对BUG的全方位解析&#xff0c;测试的执行和BUG管理&#xff01; 原来测试人员遇到BUG是这样返回给开发的&#xff01;什么是BUG&am…

ElasticSearch 批量插入漏数据

项目场景&#xff1a; 项目中需要把Mysql数据同步到ElasticSearch中 问题描述 数据传输过程中数据不时出现丢失的情况&#xff0c;偶尔会丢失一部分数据&#xff0c;本地测试也无法复现&#xff0c;后台程序也没有报错&#xff0c;一到正式环境就有问题,很崩溃 这里是批量操…

泰坦陨落2报错找不到msvcr120/msvcp120丢失实用解决方法

在计算机使用过程中&#xff0c;我们可能会遇到各种问题&#xff0c;其中之一就是msvcr120.dll缺失。msvcr120.dll是Microsoft Visual C Redistributable的一个组件&#xff0c;它包含了运行许多基于Windows的应用程序所必需的库文件。当这个文件丢失或损坏时&#xff0c;可能会…

【JAVA学习笔记】59 - JUnit框架使用、本章作业

项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter15/src/com/yinhai/homework JUnit测试框架 1.基本介绍 1. JUnit是一个Java语言的单元测试框架 2.多数Java的开发环境都已经集成了JUnit作为单元测试的工具 2.如何使用 创建方法后&#x…

LeetCode 70.爬楼梯 + 记忆化搜索 + 递推 + 动态规划 + 空间优化

关于此题的我的往期文章&#xff1a; leetCode 70.爬楼梯 动态规划_呵呵哒(&#xffe3;▽&#xffe3;)"的博客-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/133325224?spm1001.2014.3001.5501 上i-1层楼梯&#xff0c;有 dfs(i-1) 种方法&#x…

Sqlyog 无法连接 8 版本的mysql caching_sha2_password could not be loaded

Sqlyog 无法连接 8 版本的mysql caching_sha2_password could not be loaded 1.问题背景 近期系统对Mysql 版本进行了升级&#xff0c;由原来的 5.7升至 8版本&#xff0c;在现场使用Sqlyog 作为数据库连接软件时&#xff0c;发现连接失败。 2.问题现象 使用Sqlyog配置完连…

Oracle 19c 可插拔数据库PDB的创建方式

多租户容器数据库架构图总览 多租户容器数据库组成部分&#xff1a; 1.有且仅有一个CDB Root(CDB$ROOT)&#xff0c;它包含了Root和所有PDB数据库的元数据和数据字典信息。 2.有且仅有一个Seed PDB(PDB$SEED),它的作用是创建其他PDB的模板&#xff0c;它是只读库&#xff0c;…

C语言实现俄罗斯方块游戏

文章目录 1 前言2 游戏截图3 源代码 1 前言 本文介绍的是我空闲时间用C语言写的一个俄罗斯方块游戏&#xff0c;整个程序只有一个文件&#xff0c;实现了基本的游戏功能&#xff0c;但还是有些缺陷&#xff0c;希望有心之士能够继续完善&#xff0c;感谢各位&#xff01; 2 游戏…

redis rdb aof

appendonly yes # appendfsync always appendfsync everysec # appendfsync no E:\Document_Redis_Windows\redis-2.4.5-win32-win64\64bit appendonly.aof

如何实现异步通知的重试机制

工作中经常要和第三方做对接&#xff0c;比如支付、电子合同等系统。操作成功之后&#xff0c;第三方会发送异步的通知&#xff0c;返回最终的处理结果&#xff0c;使用异步而不是使用同步通知&#xff0c;是为了加快系统响应速度&#xff0c;防止线程阻塞。任务处理完成后通过…

力扣 upper_bound 和 lower_bound

&#x1f468;‍&#x1f3eb; 34. 在排序数组中查找元素的第一个和最后一个位置 &#x1f338; AC code 2023版 class Solution {public int[] searchRange(int[] nums, int target) {int[] res { -1, -1 };if(nums.length 0)return res;int l 0;int r nums.length - 1;…

GD32F303RCT6不小心将读保护开启,导致后续程序烧不进去的解决办法

这里写自定义目录标题 错误现象判断读保护开启的方法用JLink-commander查看选项字节地址处的值 解锁读保护 错误现象 用j-flash v7.68b软件通过ARM仿真器设置接口为SWD烧录编译好的目标.bin文件&#xff0c;第一次烧录成功&#xff0c;后面再也烧录不进&#xff0c;出先现象 如…

【Unity实战】最全面的库存系统(一)

文章目录 先来看看最终效果前言定义物品定义人物背包物品插槽数据拾取物品物品堆叠绘制UI移动拖拽物品选中物品跟随鼠标移动背包物品交换物品拆分物品物品堆叠完结先来看看最终效果 前言 它又来了,库存系统我前面其实一句做过很多次了,但是这次的与以往的不太一样,这个将是…

UI设计一定不能错过的4款常用工具

虽然设计审美很重要&#xff0c;但软件只是一种工具&#xff0c;但就像走楼梯和坐电梯到达顶层一样&#xff0c;电梯的效率显然更高&#xff0c;易于使用的设计工具也是如此。让我们了解一下UI设计的主流软件&#xff0c;以及如何选择合适的设计软件。 即时设计 软件介绍 即…

蓝桥白皮书16.0版——1、STEMA 考试综述

目 录 ​​​​​​​STEMA 考试综述 STEMA 考试组别 STEMA 试题数量与计分点 STEMA 考试时长 STEMA 考试成绩计算与发布 STEMA 考试成绩计算 STEMA 考试分数区间 STEMA 考试成绩百分比 STEMA 考试命题原则 STEMA 考试范围 科技素养组考试范围 推荐初级考生阅读 &#xff1…

leetcode刷题 - SQL - 简单

目录 1. 175组合两个表 左外连接 2. 181. 超过经理收入的员工 3. 182. 查找重复的电子邮箱 4. 196. 删除重复的电子邮箱 5. 197. 上升的温度 日期作差 6. 511. 游戏玩法分析 I 7. 577. 员工奖金 null条件运算 8. 584. 寻找用户推荐人 9. 586. 订单最多的客户 10. 595. 大的国家…

频谱仪超外差和零中频架构

文章目录 超外差结构零中频结构接收机结构发射机结构 优缺点对比附录相关词汇多次变频的形象解释 参考文献 频谱仪的本质就是一个超宽带、超宽调谐范围、高动态范围的通信接收机&#xff0c; 频谱仪的原理即通信接收机的原理。 遇到高频率高带宽谐波成分复杂的通信信号的话&am…

uniapp:人脸识别功能,已测试,直接复制源码,支持H5,APP安卓。不依赖任何js,SDK。

先看效果图H5: APP效果图: H5:H5端代码用.html实现,uniapp打包H5拉相机有问题,不多赘述。 时间原因没有适配,直接用的px单位, 注意:本地无法测试,必须传到线上之后,通过https访问才能正常开启摄像头!!! <!DOCTYPE html> <html lang="en">…

python二进制文件转nc(以PIOMAS海冰厚度数据为例)

一、数据下载 数据网址Polar Science Center PIOMAS Variables on Model Grid (uw.edu) 以其中的海冰厚度数据为例进行转化 点击下载需要的年份&#xff1a; 首先要明白&#xff0c;二进制文件是4个字节按顺序依次存储所有数据&#xff0c;因此heff.H1979是没有记录对应的地…