如何在Java中替换多个if语句

2020-03-28 01:49:20 1298

1. 概述

选择结构是任何编程语言的重要组成部分。但是我们编写了大量嵌套的if语句,这使得我们的代码更加复杂和难以维护。 在本教程中,我们将介绍替换嵌套if语句各种方法。 让我们探索如何简化代码的不同选项。

2. 案例研究

我们经常遇到涉及很多条件的业务逻辑,并且每个都需要不同的处理。为了演示,我们以Calculator 类为例。我们将有一个方法,它接受两个数字和一个运算符作为输入,并根据操作返回结果:

public int calculate(int a, int b, String operator) {
    int result = Integer.MIN_VALUE;
 
    if ("add".equals(operator)) {
        result = a + b;
    } else if ("multiply".equals(operator)) {
        result = a * b;
    } else if ("divide".equals(operator)) {
        result = a / b;
    } else if ("subtract".equals(operator)) {
        result = a - b;
    }
    return result;
}

我们也可以使用switch语句来实现它:

public int calculateUsingSwitch(int a, int b, String operator) {
    switch (operator) {
    case "add":
        result = a + b;
        break;
    // other cases    
    }
    return result;
}

在典型的开发中,if语句可能会变得更大,更复杂。此外,当存在复杂条件时,switch语句不适合

拥有嵌套选择结构的另一个副作用是它们变得难以管理。例如,如果我们需要添加一个新的运算符,我们必须添加一个新的if语句并实现该操作。

3. 重构

让我们探索替代选项,将上面的复杂if语句替换为更简单和易于管理的代码。

3.1 工厂类

很多时候,我们遇到选择结构,最终在每个分支中执行类似的操作。这提供了提取工厂方法的机会,该工厂方法返回给定类型的对象并基于具体对象行为执行操作

对于我们的示例,让我们定义一个具有单个apply方法的Operation接口:

public interface Operation {
    int apply(int a, int b);
}

该方法将两个数字作为输入并返回结果。让我们定义一个用于执行添加的类:

public class Addition implements Operation {
    @Override
    public int apply(int a, int b) {
        return a + b;
    }
}

我们现在将实现一个工厂类,它根据给定的运算符返回Operation的实例:

public class OperatorFactory {
    static Map<String, Operation> operationMap = new HashMap<>();
    static {
        operationMap.put("add", new Addition());
        operationMap.put("divide", new Division());
        // more operators
    }
 
    public static Optional<Operation> getOperation(String operator) {
        return Optional.ofNullable(operationMap.get(operator));
    }
}

现在,在Calculator类中,我们可以查询工厂以获取相关操作并应用源数:


public int calculateUsingFactory(int a, int b, String operator) {
    Operation targetOperation = OperatorFactory
      .getOperation(operator)
      .orElseThrow(() -> new IllegalArgumentException("Invalid Operator"));
    return targetOperation.apply(a, b);
}

在这个例子中,我们已经看到了如何将责任委托给工厂类提供的松散耦合对象。但是有可能嵌套的if语句只是转移到了工厂类,这违背了我们的目的。

或者,我们可以在Map中维护一个对象存储库,可以查询该存储库以进行快速查找。正如我们所见,OperatorFactory#operationMap服务于我们的目的。我们还可以在运行时初始化Map并将它们配置为查找。

3.2 使用枚举

除了使用Map之外,我们还可以使用Enum来标记特定的业务逻辑。之后,我们可以在嵌套的if语句或switch case 语句中使用它们。或者,我们也可以将它们用作对象的工厂并制定策略以执行相关的业务逻辑。

这样可以减少嵌套if语句的数量,并将责任委托给单个Enum值。

让我们看看我们如何实现它。首先,我们需要定义我们的枚举:

public enum Operator {
    ADD, MULTIPLY, SUBTRACT, DIVIDE
}

我们可以观察到,这些值是不同运算符的标签,将进一步用于计算。我们总是可以选择在嵌套的if语句或switch case中使用这些值作为不同的条件,但让我们设计一种将逻辑委托给Enum本身的替代方法。

我们将为每个Enum值定义方法并进行计算。例如:

ADD {
    @Override
    public int apply(int a, int b) {
        return a + b;
    }
},
// other operators
 
public abstract int apply(int a, int b);

然后在Calculator类中,我们可以定义一个执行操作的方法:

public int calculate(int a, int b, Operator operator) {
    return operator.apply(a, b);
}

现在,我们可以通过使用Operator#valueOf()方法将String值转换为Operator来调用该方法:


@Test
public void whenCalculateUsingEnumOperator_thenReturnCorrectResult() {
    Calculator calculator = new Calculator();
    int result = calculator.calculate(3, 4, Operator.valueOf("ADD"));
    assertEquals(7, result);
}

3.3 命令模式

在前面的讨论中,我们已经看到使用工厂类来返回给定运算符的正确业务对象的实例。稍后,业务对象用于在计算器中执行计算。

我们还可以设计一个Calculator#calculate方法来接受可以在输入上执行的命令。这将是替换嵌套if语句的另一种方法。

我们首先定义我们的Command接口:

public interface Command {
    Integer execute();
}

接下来,让我们实现一个AddCommand:

public class AddCommand implements Command {
    // Instance variables
 
    public AddCommand(int a, int b) {
        this.a = a;
        this.b = b;
    }
 
    @Override
    public Integer execute() {
        return a + b;
    }
}

最后,让我们在Calculator中引入一个接受并执行Command的新方法:

public int calculate(Command command) {
    return command.execute();
}

接下来,我们可以通过实例化AddCommand调用计算并将其发送到Calculator#calculate方法:

@Test
public void whenCalculateUsingCommand_thenReturnCorrectResult() {
    Calculator calculator = new Calculator();
    int result = calculator.calculate(new AddCommand(3, 7));
    assertEquals(10, result);
}

3.4 规则引擎

当我们最终编写大量嵌套if语句时,每个条件都描述了一个业务规则,必须对其进行评估才能处理正确的逻辑。规则引擎从主代码中获取了这种复杂性。一个 RuleEngine 评估规则和返回基于输入的结果。

让我们通过设计一个简单的RuleEngine来演示一个例子,该RuleEngine通过一组规则处理Expression并返回所选规则的结果。首先,我们将定义一个Rule接口:

public interface Rule {
    boolean evaluate(Expression expression);
    Result getResult();
}

其次,让我们实现一个RuleEngine:

public class RuleEngine {
    private static List<Rule> rules = new ArrayList<>();
 
    static {
        rules.add(new AddRule());
    }
 
    public Result process(Expression expression) {
        Rule rule = rules
          .stream()
          .filter(r -> r.evaluate(expression))
          .findFirst()
          .orElseThrow(() -> new IllegalArgumentException("Expression does
 not matches any Rule"));
        return rule.getResult();
    }
}

所述RuleEngine接受一个表达对象,并返回结果。现在,让我们将Expression类设计为一组包含两个Integer对象的Operator,它将被应用:

public class Expression {
    private Integer x;
    private Integer y;
    private Operator operator;        
}

最后让我们定义一个自定义的AddRule类,该类仅在指定ADD操作时进行求值:

public class AddRule implements Rule {
    @Override
    public boolean evaluate(Expression expression) {
        boolean evalResult = false;
        if (expression.getOperator() == Operator.ADD) {
            this.result = expression.getX() + expression.getY();
            evalResult = true;
        }
        return evalResult;
    }    
}

我们现在将使用Expression调用RuleEngine:

@Test
public void whenNumbersGivenToRuleEngine_thenReturnCorrectResult() {
    Expression expression = new ;
    RuleEngine engine = new RuleEngine();
    Result result = engine.process(expression);
 
    assertNotNull(result);
    assertEquals(10, result.getValue());
}

4. 结论

在本教程中,我们探索了许多不同的选项来简化复杂的代码。我们还学习了如何通过使用有效的设计模式来替换嵌套的if语句。

原文地址https://www.baeldung.com/java-replace-if-statements


Java操作Redis的常见误区

不能使用 keys * 命令不能在set中存放大量数据
2021-01-11

Java 单例模式

双重校验懒汉式单例public class Singleton { //1. 避免jvm指令重排 由于JVM具有指令重排的特性,执行顺序有可能变成 1-3-2。指令重排在单线程下不会出现问题,但是在多线程下会导致一个线程获得一个未初始化的实例。例如:线程T1执行了1和3,此时T2调用 ge
2021-01-06

Java 工厂模式

public class Rectangle implements Shape { @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } }
2021-01-02

Java 模板模式

public abstract class Template { public final void templ() { System.out.println("开始"); code(); System.out.println("结束"); } public abstract v
2021-01-02

Java 装饰者模式

接口Printerpublic interface Printer { void start(); void print(); void stop(); } 实现类 HPPrinter public class HPPrinter implements Printer {
2021-01-02
Java Proxy.newProxyInstance动态代理

Java Proxy.newProxyInstance动态代理

定义接口public interface Student { void buy(); String talk(); } 实现类public class Lisi implements Student { @Override public void buy() { System.ou
2021-01-02

Java 解析windows 快捷方式 lnk文件

转载自 https://www.oschina.net/code/snippet_12_274import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; public c
2020-12-10

Spring Boot项目中使用RedisTemplate.delete() 删除指定key失败的解决办法

https://blog.csdn.net/hello_world_qwp/article/details/85763286上面这篇博客扯一大堆, 居然还分析源码实际只是自定义了key的序列化方式导致最终操作redis的时候序列化的key与预期的key不一致而已, 自然就删不掉redis中的数据了
2020-11-26

jpa方法参数必须加上@Param

void deleteByKl(String kl);线上可能报错原因可能是编译时没有加-parameters这个参数, 编译后丢失了参数名称, 使得反射拿不到对应参数需要加上注解void deleteByKl(@Param("kl") String kl);同理public ResultVO de
2020-09-28

日志规范

日志中要打印参数错误示例 @GetMapping("/share_coupon") public ActionResult shareCoupon(Long couponSn) { //validate code try { retur
2020-09-17

Java中的Date和时区转换

1.Date中保存的是什么在java中,只要我们执行 Date date = new Date(); 就可以得到当前时间。如:Date date = new Date(); System.out.println(date); 输出结果是: Thu Aug 24 10:15:29 CST 2017 也
2020-03-28
基于自定义注解手写权限控制

基于自定义注解手写权限控制

方法一: AOP 方法二: 拦截器项目结构项目依赖<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-w
2020-03-28

如何在Java中替换多个if语句

1. 概述选择结构是任何编程语言的重要组成部分。但是我们编写了大量嵌套的if语句,这使得我们的代码更加复杂和难以维护。 在本教程中,我们将介绍替换嵌套if语句的各种方法。 让我们探索如何简化代码的不同选项。2. 案例研究我们经常遇到涉及很多条件的业务逻辑,并且每个都需要不同的处理。为了演示,我们以C
2020-03-28
Java中方法的参数传递机制

Java中方法的参数传递机制

来看一段代码 public class Man { private String name; private Integer age; public String getName() { return name; } publi
2020-03-28

解决VM虚拟机启动后假死

一、开机后黑屏假死管理员cmd 输入netsh winsock reset,回车,重启系统二、启动后假死关闭本机防火墙
2020-03-27

如何快速上手一个项目

不知道你有没有经历过一个五年或者更长工作年限的开发人员半路加入团队的情况,可能第一两个星期他会问一些业务或者技术问题,不过一两个月他就可能在指导那些初级开发人员了。什么原因呢?因为他已经从过往经验里面总结出来一些套路了。那么套路是什么呢?绝大部分业务系统,不管他后端是oracle、mysql、nos
2020-03-28

每日邮件 推送天气、鸡汤、博客

功能邮件提醒每日天气每日鸡汤每日博客邮件格式你好,xxx,今天是2019年7月21日,农历六月十五 【每日天气】 城市[北京] 天气[晴]\n温度[31℃ ~ 24℃] 白天风力[东南风<3级] 夜间风力[东北风<3级] 紫外线指数[最弱--辐射弱,涂擦SPF8-12防晒护肤品]\n穿衣指数[短袖
2020-03-28

SpringDataRedis 常用操作

//向redis里存入数据和设置缓存时间 stringRedisTemplate.opsForValue().set("test", "100",60*10,TimeUnit.SECONDS); //根据key获取缓存中的val stringRedisTemplate.opsForValue().
2020-03-28

js向json中添加属性

object['attr'] = {};
2020-03-28

如何在Java中替换多个if语句

1. 概述选择结构是任何编程语言的重要组成部分。但是我们编写了大量嵌套的if语句,这使得我们的代码更加复杂和难以维护。 在本教程中,我们将介绍替换嵌套if语句的各种方法。 让我们探索如何简化代码的不同选项。2. 案例研究我们经常遇到涉及很多条件的业务逻辑,并且每个都需要不同的处理。为了演示,我们以C
2020-03-28

SpringBoot Undertow http跳转https

package com.mtons.mblog.config; import io.undertow.Undertow; import io.undertow.UndertowOptions; import io.undertow.servlet.api.SecurityConstraint; i
2020-03-28

Java中的Date和时区转换

1.Date中保存的是什么在java中,只要我们执行 Date date = new Date(); 就可以得到当前时间。如:Date date = new Date(); System.out.println(date); 输出结果是: Thu Aug 24 10:15:29 CST 2017 也
2020-03-28
解决SpringBoot+JPA中使用set方法时自动更新数据库问题

解决SpringBoot+JPA中使用set方法时自动更新数据库问题

首先引入EntityManager:然后用它来强转获得HibernateEntityManager,然后调用获得Session,然后在set完之后用Session的.evict()方法清掉该对象缓存(并非所有对象缓存),如此就ok了...好吧,已经java11不建议用了,不过还能用,先用着吧!更新了
2020-04-04