【JAVA】优化if else的几种方式

news/2024/5/18 21:29:27 标签: 优化if else, Optional, 枚举, 表驱动, 重构

在代码编写初期,我们写出来的代码,脉络清晰,结构简单。可随着bug或者新需求的出现,状态变得越来越多,只能不停地加else来区分,久而久之,判断的次数越来越多,嵌套的层数也越来越深,变得难以维护。

当我们狠下心来决定改造时,有哪些方法能够优化if else结构呢?


第一种方法:提前return,减少else判断

优化前:

    private int handlePre1(boolean flag) {
        if (flag) {
            //do something
        } else {
            //do something
            return -1;
        }
        return 0;
    }

优化后:

    private int handleAfter1(boolean flag) {
        if (!flag) {
            //do something
            return -1;
        }
        //do something
        return 0;
    }

这个做法,将可以return的状态提到if中,直接干掉了else!


第二种方法:使用三目运算符

优化前:

    private int handlePre2(boolean flag) {
        if (flag) {
            return 0;
        } else {
            return 1;
        }
    }

优化后:

    private int handleAfter2(boolean flag) {
        return flag ? 0 : 1;
    }

这种方式局限性比较大,但对于那些依据状态直接返回某个值的情况非常适用,优化后代码非常简洁。如果三目运算符中还嵌套一层三目运算符,建议就不要使用这种方式了。


第三种方法:使用Optional

我们在代码中,经常需要判断某个对象是否为null,不为null后才会进行接下来的操作,好在java8为我们提供了Optional类。

优化前:

        User user = new User();
        int age = 20;
        if (user != null) {
            age = user.getAge();
        }

优化后:

        User user = new User();
        Optional<User> optionalUser = Optional.ofNullable(user);
        Integer age = optionalUser.map(User::getAge).orElse(20);

Optional让非空校验更加有优雅,代码层面减少了if判断,其实Optional在底层为我们封装好了if null判断而已。


第四种方法:使用switch case

优化前:

    private String getCn(String en) {
        String cn;
        if ("student".equals(en)) {
            cn = "学生";
        } else if ("teacher".equals(en)) {
            cn = "教师";
        } else {
            cn = "未知";
        }
        return cn;
    }

同样,我们可以借鉴方式一提前return,来代替else

    private String getCnByEarlyReturn(String en) {
        if ("student".equals(en)) {
            return "学生";
        }
        if ("teacher".equals(en)) {
            return "教师";
        }
        return "未知";
    }

如果这里面的情况很多,我们可能依然要判断多次,那我们用switch改写。

优化后:

    private String getCnBySwitch(String en) {
        switch (en) {
            case "student":
                return "学生";
            case "teacher":
                return "教师";
            default:
                return "未知";
        }
    }

如果单从效率来看,之前的时间复杂度为O(n),switch的复杂度为O(1),本质上swicth是通过比对String的哈希码来实现的。


第五种方法:使用枚举

如果上一例中的student、teacher是常量的话,最好是定义在枚举里。

    public enum CnEnum {

        STUDENT("student", "学生"),
        TEACHER("teacher", "教师"),
        UNKNOWN("unKnown", "未知");

        private String en;
        private String cn;

        public String getEn() {
            return en;
        }

        public String getCn() {
            return cn;
        }

        CnEnum(String en, String cn) {
            this.en = en;
            this.cn = cn;
        }

        static String of(String en) {
            for (CnEnum temp : CnEnum.values()) {
                if (temp.getEn().equals(en)) {
                    return temp.getCn();
                }
            }
            return CnEnum.valueOf("UNKNOWN").getCn();
        }
    }

最后这样调用即可:

        String cn = CnEnum.of("student");

第六种方法:表驱动

表驱动法是指我们可以将信息存在于表中,从表中取,而不必用if else逻辑去寻找,大可以将这里的“表”理解为一种容器。

例如我们可以将每月的天数存在数组中,需要时,直接从数组中获取,而不是用if else输出。

或者依据不同的字符串,执行不同的处理逻辑。if else版本如下:

        String profession = "student";
        if ("student".equals(profession)) {
           //do something
        } else if ("teacher".equals(profession)) {
            //do something
        } else if ("programmer".equals(profession)) {
            //do something
        }

在这种情况下,使用表驱动法后,可以让Student、Teacher等类实现某一个共同的接口,在实现方法里,书写各自的逻辑。然后利用Spring强大的自动注入,会注入到Map<组件名称,组件实例>的map里,之后直接根据组件名称来获取到对应的实例,最后调用各自的逻辑。

这个例子可以先异步到我的另外一篇文章【SpringBoot】使用不同的策略动态地调用某个接口的实现类


第七种方法:策略模式+工厂模式

首先给定以下的一个场景,传入一个职业,输出职业的主要工作

一般我们会这么写:

        String profession = "student";
        if ("student".equals(profession)) {
            System.out.println("学习");
        } else if ("teacher".equals(profession)) {
            System.out.println("教书");
        } else if ("programmer".equals(profession)) {
            System.out.println("写代码");
        }
        //.......以后可能会加上各种各样的职业

那么,没增加一种职业,就会增加一个if else判断,代码可读性差,日后难以维护。

现在使用策略模式+工厂模式来改造它。

  • 首先定义一个职业接口,里面定义一个输出职责的方法
  • 之后每增加一种职业,都实现职业接口,实现输出职责的方法(策略模式)
  • 接着定义一个工厂类,用来根据具体情况生产各种职业类(工厂模式)

职业接口:

public interface Profession {

    //输出职责
    void output();

}

实现类:

public class Student implements Profession {

    @Override
    public void output() {
        System.out.println("学习");
    }

}


public class Teacher implements Profession {

    @Override
    public void output() {
        System.out.println("教书");
    }

}


public class Programmer implements Profession {

    @Override
    public void output() {
        System.out.println("写代码");
    }

}

工厂类:

public class ProfessionFactory {

    private static Map<String, Profession> map = new HashMap<>();
    //未知职业
    private static final Profession DEFAULT_PROFESSION = new DefaultProfession();

    static {
        map.put("student", new Student());
        map.put("teacher", new Teacher());
        map.put("default", DEFAULT_PROFESSION);
    }

    public static Profession getProfession(String s) {
        Profession profession = map.get(s);
        return profession == null ? DEFAULT_PROFESSION : profession;
    }

    static class DefaultProfession implements Profession {

        @Override
        public void output() {
            System.out.println("职责未知");
        }

    }
}

优化后:

        Profession profession = ProfessionFactory.getProfession("student");
        profession.output();

之后每增加一种职业,只要实现Profession接口就好,并在ProfessionFactory中注册自己。使用策略模式+工厂模式,再也不需要为日益增长的if else头疼了。

当然,这里的key值,可以设置为常量。


总结

过多的if else严重降低可读性与可维护性,所以在一开始,就要依据实际情况,灵活地选择处理方式。


http://www.niftyadmin.cn/n/1705275.html

相关文章

财务一体化项目,进度与计划20

补充昨天的进度表&#xff0c;昨天弄了一天photoshop与dreamweaver&#xff0c;结果那个登陆界面还是没设计出来&#xff0c;由于进度没跟上计划&#xff0c;于是就懒得写进度表了。还好&#xff0c;今天一天没有课&#xff0c;一定要赶上计划。今天把登陆界面和普通用户的使用…

【JAVA】Lambda执行原理

在我的想法里&#xff0c;Lambda长得很奇怪&#xff0c;虚拟机真的认识这玩意吗&#xff1f;还是说&#xff0c;Lambda经过编译后&#xff0c;脱掉了伪装的衣服&#xff0c;变成了大家熟知的方法&#xff1f; 对Lambda不熟悉的同学&#xff0c;可以先看我的另外两篇文章。 【…

Oracle数据库完整卸载

1、停止所有Oracle服务 进入计算机管理&#xff0c;在服务中&#xff0c;找到oracle开头的所有服务&#xff0c;右击选择停止。 快捷键&#xff1a;ctrlshiftesc打开任务管理器 2、打开Universal Installer工具运行卸载Oracle数据库程序 点击开始菜单找到Oracle,然后点击Or…

为Office文档添加Windows窗体控件:在运行时向应用程序级外接程序中添加控件

可以在运行时以编程方式向任何打开的文档中添加 Windows 窗体控件。首先&#xff0c;生成一个基于所打开的文档或工作表的宿主项。然后&#xff0c;使用新宿主项的 Document..::.Controls 属性的方法&#xff08;在 Word 中&#xff09;&#xff0c;或者使用新宿主项的 Workshe…

各种文件后缀的意义(持续更新中)

.properties是什么&#xff1f; 后缀properties是一种属性文件。 这种文件以keyvalue格式存储内容&#xff0c;Java中可以使用Properties类来读取这个文件&#xff0c;就能得到对应的数据。 String valuep.getProperty(key);一般这个文件作为一些参数的存储&#xff0c;代码就…

男人最帅的42个瞬间[值得学习](转)

男人最帅的42个瞬间[值得学习](转) 1.不说话或者安静思考的时候。沉默但目光专注地看你说话的男人&#xff0c;一定内心丰富&#xff0c;这一刻的男人&#xff0c;因为他身上似乎有一种至命的磁场&#xff0c;让你觉得他酷极了。 2.含着半口饮料微笑。为了回应你&#xff0c;他…

如何向Word文档添加操作窗格

适用对象 本主题中的信息仅适用于指定的 Visual Studio Tools for Office 项目和 Microsoft Office 版本。 项目类型 文档级项目 Microsoft Office 版本 Word 2007 Word 2003 有关更多信息&#xff0c;请参见按应用程序和项目类型提供的功能。 “添加新项”对话框包含一个…

终于解决住房问题了

清明节&#xff08;4月4日&#xff09;过去了&#xff0c; "如果你死后&#xff0c;墓志铭打算写点啥&#xff1f;"以下是一些回复&#xff1a;1.一居室&#xff0c;求合租&#xff0c;面议。2.小事招魂&#xff0c;大事挖坟。3.我觉得我还可以抢救一下&#xff01;4…