根据不同参数调用对应的方法《七种解决办法》,减少if语句

文章目录

    • 前言
    • 一、三目运算符
    • 二、switch case
    • 三、枚举
    • 四、反射
      • 1、创建两个方法名不同的方法
      • 2、调用
    • 五、表驱动法+策略模式(两种实现方式)
      • 1、先创建一个接口
      • 2、两个实现类分别实现上面那个接口
      • 3.1、直接使用Map注入
      • 3.2、也可使用getBean进行调用
    • 六、表驱动法+函数接口
      • 1、优化前:
      • 2、优化后:
    • 七、使用Java8的新特性Optional判断是否为null
      • 1、优化前:
      • 2、优化后:
      • 3、也可以使用Optional进行一个责任链
      • 说明:
    • 结束语

前言

关于为啥写这一篇博客呢!三个答案…

第一个是我上个星期参加了公司的代码评审会议,在会议上就我写的服务展开了分析,结论是别的都还好,就是if...else 太多了,影响阅读,而且不美观,对的,就是不美观,在公司来说,这也是很重要的一点,代码整洁度直接影响到阅读体验,而且我自己在给别人解释时,也会有因为这种情况而短路的情况;

第二个就是我自己也挺恶心的,还有就是我朋友写了一篇这样的文章,看了一下,收益匪浅,但总感觉差了点东西

第三个也是最重要的一点,这个月还没写过一篇呢,哈哈,被我朋友催着学习了

那么…就开始吧!!

一、三目运算符

三目运算符是基础不做过多解释,直接看代码

优化前:

Integer test6(String param){
    if (param.equals("string")){
        return 1;
    }else {
        return 0;
    }
}

优化后:

Integer test6(String param){
    return param.equals("string") ?1:0;
}

二、switch case

switch也是基础语句,不做过多解释,直接看代码

优化前:

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

优化后:

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

三、枚举

如果传进来的参数是在指定范围的,那最好定义在枚举中

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

也可以调用isValidEnumIgnoreCase()方法判断指定参数是否是有效枚举,可以简单理解成枚举类中是否包含该元素

 //判断来源是否符合规范
if (EnumUtils.isValidEnumIgnoreCase(CnEnum.class,参数)) {
    //包含
    return true;
}

四、反射

场景: 根据指定参数调用对应的方法

思路: 其实这个方法在以前刚学习反射时就已经用过,就是创建当前对象的反射对,调用getDeclaredMethod()方法获取方法对象,然后调用invoke()执行就可

可以把方法名添加到枚举中,参数进来时先调用枚举的isValidEnumIgnoreCase()方法进行过滤

1、创建两个方法名不同的方法

public String student(String param){
    return "调用学生:"+param+"方法成功";
}

public String teacher(String param){
    return "调用老师:"+param+"方法成功";
}

2、调用

public String test(String param) throws Exception {
    //可以把方法名添加到枚举中,参数进来时先调用枚举的`isValidEnumIgnoreCase()`方法进行过滤
    //获取当前对象的反射对象
    Class<? extends DemoServiceImpl> aClass = this.getClass();
    //获取方法对象
    Method method = aClass.getDeclaredMethod(param, String.class);
    //执行方法
    String invoke = (String) method.invoke(this, param);
    return invoke;
}

五、表驱动法+策略模式(两种实现方式)

表驱动法是指我们可以将信息、方法、以及对象存在表中,这个表可以是list,可以是map,也可以是数组,更可以是spring容器…,从而我们可以直接从表中取,而不需要if…else

1、先创建一个接口

public interface UserService {

    //获取用户的主要任务
    String getTask();
    
}

2、两个实现类分别实现上面那个接口

@Service("student")
public class StudentServiceImpl implements UserService {
    @Override
    public String getTask() {
        return "学生在学习";
    }
}


@Service("teacher")
public class TeacherServiceImpl implements UserService {
    @Override
    public String getTask() {
        return "老师在教书";
    }
}

3.1、直接使用Map注入

在服务启动后,userMap就会存在两个元素,(“student”,StudentServiceImpl)(“teacher”,TeacherServiceImpl)

Spring会自动地将形如(@Service后面的名称,实现该接口的类)注入到该userMap中

@Service
public class UserContext {

    @Autowired
    Map<String, UserService> userMap;
     
    public UserService getUserService(String type) {
        return userMap.get(type);
    }

}

3.2、也可使用getBean进行调用

从应用的上下文中获取指定bean对象,相信各位同学对spring容器都熟悉

/**
 * 获取不同bean对象
 */
@Resource
private ApplicationContext applicationContext;

public UserService create(String type) {
    return applicationContext.getBean(type, UserService.class);
}
@Service
public class UserContext {
     
    public String getUserService(String type) {
       UserService userService = create(type);
       String task = userService.getTask();
       return task;
    }

}

六、表驱动法+函数接口

表驱动法在第五点里已经解释过了

java8很重要的新特性 Lamdba,同时也伴随着一种函数式编程的说话,这里主要举例说明两种函数

  1. Function:有输入,有输出,T代表输入,R代表输出
  2. Supplier:只要输出没有输入
  3. 想了解其他函数可以查看我上一篇博客:Lamdba表达式详解(一篇解决lamdba表达式)

1、优化前:

public String student(String param){
    return "调用学生:"+param+"方法成功";
}

public String teacher(String param){
    return "调用老师:"+param+"方法成功";
}

//调用
public String test(String param){
    if (param.equals("student")){
        return student(param);
    }else if (param.equals("teacher")){
        return teacher(param);
    }else {
        return "找不到对应的方法";
    }
}

2、优化后:

当方法都有输入和输出时,我们就使用Function函数,调用apply()方法执行

public String student(String param){
    return "调用学生:"+param+"方法成功";
}

public String teacher(String param){
    return "调用老师:"+param+"方法成功";
}

//调用,如果没有输入,就是用Supplier函数,一样的用法
public String test(String param){
  //创建一个map,value为function函数,第一个string为入参,第二个string为返回对象
  HashMap<String, Function<String, String>> map = new HashMap<>();
    map.put("student",this::student);
    map.put("teacher",this::teacher);

    return map.get(param).apply(param);
}

还可以使用代码块启动就加入map中,如果是多次使用的话建议这样,但只是使用一次的话还是建议直接写在方法里;

private static HashMap<String, Function<String, String>> map = new HashMap<>();

{
    map.put("student",this::student);
    map.put("teacher",this::teacher);
}

//调用
public String test(String param){
    return map.get(param).apply(param);
}

七、使用Java8的新特性Optional判断是否为null

Optional是Java8的新特性,他可以对元素进行非空判断,然后执行对应的方法或者只做判断,也可以结合函数进行操作,但不能很好的结合stream,下面是Optional类库的一些方法说明:

方法 描述
of 把指定的值封装为Optional对象,如果指定的值为null,则抛出NullPointerException
empty 创建一个空的Optional对象
ofNullable 把指定的值封装为Optional对象,如果指定的值为null,则创建一个空的Optional对象
get 如果创建的Optional中有值存在,则返回此值,否则抛出NoSuchElementException
orElse 如果创建的Optional中有值存在,则返回此值,否则返回一个默认值
orElseGet 如果创建的Optional中有值存在,则返回此值,否则返回一个由Supplier接口生成的值
orElseThrow 如果创建的Optional中有值存在,则返回此值,否则抛出一个由指定的Supplier接口生成的异常
filter 如果创建的Optional中的值满足filter中的条件,则返回包含该值的Optional对象,否则返回一个空的Optional对象
map 如果创建的Optional中的值存在,对该值执行提供的Function函数调用
flagMap 如果创建的Optional中的值存在,就对该值执行提供的Function函数调用,返回一个Optional类型的值,否则就返回一个空的Optional对象
isPresent 如果创建的Optional中的值存在,返回true,否则返回false
ifPresent 如果创建的Optional中的值存在,则执行该方法的调用,否则什么也不做

1、优化前:

String test6(String param){
    if (param != null){
        return param;
    }else {
        return "错误";
    }
}

2、优化后:

String test6(String param){
   return Optional.ofNullable(param).orElse("错误");
}

查看底层我们可以发现,其实就是一个三目运算

public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}

3、也可以使用Optional进行一个责任链

Optional的真正作用并不是在 “告诉观众这个数据有可能为null” 上,而是提供了一个可以把多个结果有可能为null的操作串起来最终得到一个有可能为null结果的容器。而这种写法的真正好处并不是“这堆东西其中一步可能是null”,而是明确告诉观众“这堆操作是一个逻辑整体而且它们形成了一个pipe(前者的输出是且只是后者的输入)”

例如Optional最经典的null-safe get chain。

return Optional.ofNullable(a)
    .map(A::getB)
    .map(B::getC)
    .map(C::getD)
    .orElse(null);

很明显像上面这样写多多少少有点不严谨,我们举例说明:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.math.BigDecimal;

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Leader {

    private Long employeeId;
    private BigDecimal bonus;

}


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Employee {

    private Long id;
    private String name;
    private Boolean leader;

}



import java.util.Optional;
 /**
     * 找到ID为1的员工,如果有奖金就打印出来,没有就打印没有奖金;
     * @throws
     */
public class FiltUtil {

    public void OptionalStudy() {
        Optional<Leader> leader = Optional.ofNullable(getEmployeeById(1L)
                .filter(Employee::getLeader)
                .map(Employee::getId)
                .flatMap(this::getLeaderByEmployeeId)
                .orElse(null));
        if (leader.isPresent()) {
            Optional.of(leader.map(Leader::getBonus).map(bonus -> String.format("员工ID为1的leader奖金为:%s", bonus)).orElse("员工ID为1的leader也没有奖金")).ifPresent(System.out::println);
        } else {
            System.out.println("员工ID为1的leader未找到,他可能只是一个基层员工,不配拥有奖金");
        }
    }

    private Optional<Employee> getEmployeeById(Long id) {
        //return Optional.of(new Employee(1L, "大老板", Boolean.TRUE));
        return Optional.of(new Employee(1L, "大老板", Boolean.FALSE));
    }

    private Optional<Leader> getLeaderByEmployeeId(Long employeeId) {
        //return employeeId == 1L ? Optional.of(new Leader(1L, BigDecimal.valueOf(1000000000))) : Optional.empty();
        return employeeId == 1L ? Optional.of(new Leader(1L, null)) : Optional.empty();
    }

//主函数测试
    public static void main(String[] args) {
        FiltUtil filtUtil = new FiltUtil();
        filtUtil.OptionalStudy();

    }
}

说明:

其实本人不是很喜欢用Optional,因为它虽然使你的代码优雅了,但失去了逻辑性,对后期的维护,以及交接都很不利,而且如果你只是用来非空判断的话,很明显有别的更好的选择,例如:apache的工具类,hutool的工具类,都比Optional更直接

所以对于Optional得态度就是喜欢就用,不喜欢就不用

结束语

所有方法没有说哪个比哪个好,都是在不同的场景,就会有不同的选择,希望同学们理性使用,有任何问题都可以评论或私聊,最后送上《阿里云的这群疯子》中的一句话:

参考资料:if…else 代码优化 提高代码质量–进阶之路

任何执拗都会成为过往,只有时间会告诉你对错。

你可能感兴趣的:(java学习,java,spring,后端)