一、简介
我们平时在写代码的时候,if-else判断语句基本上必不可少,当我们的判断语句只有一两层的时候,类似下面这种,情况还好,基本上能接受;
if(condition){
doSomeThing();
} else {
doSomeThing();
}
但是当过度的使用if-else的时候,会对代码的可读性、可扩展性造成负面影响,比如类似下面这个!
if(condition1){
if(condition2){
if(condition3){
if(condition4){
if(condition5){
if(condition6){
doSomeThing();
}
}
}
}
}
}
如果判断语句越来越多,后期进行项目维护也会比较困难,对于后面接手项目的人来说,会是一个很头疼的问题了。
因此,如何去除掉代码中过多的if...else语句,反映的是程序员对软件重构、设计模式、面向对象设计、架构模式、数据结构等多方面技术的综合运用能力。
那问题来了,如何解决代码中过多的if...else语句呢?下面一起来聊聊有效的几种解决方案!
二、解决方案
首先,我们编写一个简单的程序计算器,代码如下!
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;
}
以上文为案例,我们一起来看看,可以对其进行改造的几种方式!
2.1、适时的 return
如果if里面条件判断比较单一又互斥的,我们可以在合适的位置直接return,废除掉else代码,比如将其改造成如下方式:
public int calculate(int a, int b, String operator) {
if ("add".equals(operator)) {
return a + b;
}
if ("subtract".equals(operator)) {
return a - b;
}
if ("multiply".equals(operator)) {
return a * b;
}
if ("divide".equals(operator)) {
return a / b;
}
return 0;
}
代码瞬间是不是清晰了很多!
2.2、引入策略模式进行改造
但是当if判断逻辑内容非常复杂的时候,以上的方案就有点不够优雅了,这个时候,我们可以将if判断逻辑独立成类来单独处理,操作方法如下!
- 首先,我们定义一个Operation接口,用于逻辑的计算
public interface Operation {
/**
* 执行计算
* @param a
* @param b
* @return
*/
int execute(int a, int b);
}
- 接着,分别将四个if判断逻辑独立成一个模块,来单独处理
public class AddOperation implements Operation {
@Override
public int execute(int a, int b) {
return a + b;
}
}
public class SubOperation implements Operation {
@Override
public int execute(int a, int b) {
return a - b;
}
}
public class MultiOperation implements Operation {
@Override
public int execute(int a, int b) {
return a * b;
}
}
public class DivOperation implements Operation {
@Override
public int execute(int a, int b) {
return a / b;
}
}
- 然后,创建一个工厂类,用于处理客户端传入的参数
public class OperatorFactory {
private static Map<String, Operation> operationMap = new HashMap<>();
static {
//初始化实现类
operationMap.put("add", new AddOperation());
operationMap.put("sub", new SubOperation());
operationMap.put("multi", new MultiOperation());
operationMap.put("div", new DivOperation());
// more operators
}
/**
* 获取对应的目标实现类
* @param operator
* @return
*/
public static Optional<Operation> getOperation(String operator){
return Optional.ofNullable(operationMap.get(operator));
}
}
- 最后,在需要的地方引入方法即可!
public class OperatorTestMain {
public static void main(String[] args) {
//获取计算的目标实现类
Operation targetOperation = OperatorFactory
.getOperation("add")
.orElseThrow(() -> new IllegalArgumentException("Invalid Operator"));
int result = targetOperation.execute(1, 2);
System.out.println("result:" + result);
}
}
至此,if...else方法改造完毕!
2.3、引入规则引擎模式进行改造
当方法内部的if...else越来越多的时候,我们可以通过规则引擎模式来消除这种复杂度,具体实践如下!
- 首先,创建一个规则标准接口Rule,并将条件判断方法进行抽象
public interface Rule {
/**
* 检查是否进入规则计算
* @param expression
* @return
*/
boolean evaluate(Expression expression);
/**
* 执行规则计算
* @param expression
* @return
*/
int execute(Expression expression);
}
public class Expression {
private Integer a;
private Integer b;
/**
* 计算类型
*/
private String operator;
public Expression(Integer a, Integer b, String operator) {
this.a = a;
this.b = b;
this.operator = operator;
}
}
- 接着,根据每个规则创建不同的计算实现类,以AddRule为例
public class AddRule implements Rule {
@Override
public boolean evaluate(Expression expression) {
if ("add".equalsIgnoreCase(expression.getOperator())) {
return true;
}
return false;
}
@Override
public int execute(Expression expression) {
int result = expression.getA() + expression.getB();;
return result;
}
}
然后,创建一个规则引擎,用于逻辑的处理
public class RuleEngine {
private static List<Rule> rules = new ArrayList<>();
static {
rules.add(new AddRule());
//添加其他的规则计算实现类
}
/**
* 执行规则计算
* @param expression
* @return
*/
public int process(Expression expression){
Rule rule = rules
.stream()
.filter(r -> r.evaluate(expression))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Expression does not matches any Rule"));
return rule.execute(expression);
}
}
最后,在需要的地方引入方法即可!
public class RuleEngineTestMain {
public static void main(String[] args) {
Expression expression = new Expression(1, 2, "add");
RuleEngine engine = new RuleEngine();
int result = engine.process(expression);
System.out.println("result:" + result);
}
}
规则引擎和策略模式,最大的不同的就是将条件逻辑判断抽象化,由具体的实现类来判断是不是满足要求,如果满足要求,就执行;否则不执行!
三、Spring 集成应用
可能有的同学会发出疑问,以上介绍的都是原生的处理办法,在当下处处都集成了Spring框架的项目,我们应该如何使用呢?
下面我们以上面提到的《引入策略模式进行改造》为例,如果在Spring里面,我们应该如何应用?
3.1、Spring 策略模式实现介绍(方案一)
- 首先,我们还是定义一个Command接口,用于方法的抽象和统一
public interface Command {
/**
* 命令类型
* @return
*/
String operateType();
/**
* 执行
* @param a
* @param b
* @return
*/
Integer execute(int a, int b);
}
- 接着,编写四套不同的计算处理逻辑
@Component
public class AddCommand implements Command {
@Override
public String operateType() {
return "add";
}
@Override
public Integer execute(int a, int b) {
return a + b;
}
}
@Component
public class SubCommand implements Command {
@Override
public String operateType() {
return "subtract";
}
@Override
public Integer execute(int a, int b) {
return a - b;
}
}
@Component
public class MultiCommand implements Command {
@Override
public String operateType() {
return "multiply";
}
@Override
public Integer execute(int a, int b) {
return a * b;
}
}
@Component
public class DivCommand implements Command {
@Override
public String operateType() {
return "divide";
}
@Override
public Integer execute(int a, int b) {
return a / b;
}
}
- 然后,编写一个类似于上文的策略处理类
@Component
public class CalculatorService implements ApplicationContextAware {
private Map<String, Command> commandMap = new ConcurrentHashMap<>();
/**
* 执行计算
* @param operateType
* @param a
* @param b
* @return
*/
public int calculate(String operateType,int a, int b){
Command targetCommand = Optional.ofNullable(commandMap.get(operateType))
.orElseThrow(() -> new IllegalArgumentException("Invalid Operator"));
return targetCommand.execute(a,b);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, Command> tempMap = applicationContext.getBeansOfType(Command.class);
tempMap.values().forEach(source -> commandMap.put(source.operateType(), source));
}
}
- 最后,我们只需要在适当的位置应用即可!
@RunWith(SpringRunner.class)
@SpringBootTest
public class CalculatorServiceTest {
@Autowired
private CalculatorService calculatorService;
@Test
public void test(){
int result = calculatorService.calculate("add", 1,2);
System.out.println("result:" + result);
}
}
3.2、Spring 策略模式实现介绍(方案二,推荐)
翻查Spring的ioc容器,你会发现一个秘密,当一个接口有多个实现类时,Spring会自动将Strategy接口的实现类注入到这个Map中,key为bean id,value值则为对应的策略实现类。
简单的说,我们只需要通过@Autowired注入对象,不需要通过CalculatorService这个类进行单独配置,操作方式如下!
- 首先,编写一个CommandFactory工厂类,用于逻辑的处理
@Component
public class CommandFactory {
/**
* Spring会自动将Strategy接口的实现类注入到这个Map中,key为bean id,value值则为对应的策略实现类
*/
@Autowired
private Map<String, Command> commandMap;
/**
* 执行计算
* @param operateType
* @param a
* @param b
* @return
*/
public int calculate(String operateType,int a, int b){
Command targetCommand = Optional.ofNullable(commandMap.get(operateType))
.orElseThrow(() -> new IllegalArgumentException("Invalid Operator"));
return targetCommand.execute(a,b);
}
}
- 最后,直接在合适的地方使用CommandFactory即可!
@RunWith(SpringRunner.class)
@SpringBootTest
public class CalculatorServiceTest {
@Autowired
private CommandFactory commandFactory;
@Test
public void test(){
int result = commandFactory.calculate("addCommand", 1,2);
System.out.println("result:" + result);
}
}
四、小结
本文主要围绕如何解决if...else...过多问题进行一些总结和案例分享,期望能对大家有所帮助!
五、参考
1、baeldung java-replace-if-statements
2、知乎 - 如何去除代码中过多的if语句