网站首页 > java教程 正文
1. 概述
决策结构在大多数编程语言中占据了至为重要的一步。但是我们常常会被大量的那种让代码变得难读且难维护的内嵌if语句搞得浑身难受。
在这次的教程中,我们将来过一下可以代替内嵌if语句的各种方法。让我们来探索简化我们代码的途径吧。
2. 案例学习
通常我们会遇到一些需要做一系列条件处理的业务逻辑,并且它们每一个都需要不同的处理。
为了演示,我们来看一下Calulator(计算器)类的一个例子。上面是带有两个数字类型参数,一个操作符参数以及基于操作的数值返回值的一个方法:
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语句也并非所有场景都适用,当条件复杂的时候,switch语句就没什么作用了。
另一个使用嵌套条件声明编程的影响是它使得程序变得难以管理。例如,如果我们需要新添加一个操作,我们需要添加一个新的if条件以及条件的实现。
3. 重构
让我们尝试下使用其他更简洁和易管理的方法来代替这个复杂的if语句吧。
3.1 工厂类
很多时候我们经常遇见许多条件声明,它们是用来处理每个分支中相似的操作。这提供给我们一个想法,提取一个返回具体类型的对象并且根据具体对象行为执行操作的工厂类。
例如在下面,让我们定义一个带有单独的apply方法的操作接口
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; } }
我们现在将要实现一个返回基于给定操作符的操作实例的工程类:
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,来达成我们的目的。我们也能够在运行时定义映射对象并且配置它们用于查找。
3.2 使用枚举
除了映射对象(map)的使用之外,我们也可以使用枚举来标记特定的逻辑业务。在这之后,我们能通过它来代替嵌套if语句或者swtich语句了。作为其他处理,我们也可以使用它们作为对象工厂并且整理用于处理相关的业务逻辑操作。
这会减少嵌套if语句的数量并且将业务责任委托给独立的枚举变量中。
让我们来看看怎么去实现它。首先,我们需要定义一个枚举类:
public enum Operator { ADD, MULTIPLY, SUBTRACT, DIVIDE }
像我们看到这样,这些值是不同操作符的标签,并且会运用到之后的计算当中。就像嵌套if语句和switch语句那样,我们可以将这些值当作选项来使用。但和它们不同的地方,让我们去设计一种能够将逻辑委托给枚举本身的替代方法吧。
我们为每一个枚举量都定义了各自的方法并且进行了计算操作,例如:
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()方法来将字符串转换为操作符来调用方法了:
@Test public void whenCalculateUsingEnumOperator_thenReturnCorrectResult() { Calculator calculator = new Calculator(); int result = calculator.calculate(3, 4, Operator.valueOf("ADD")); assertEquals(7, result); }
3.3 命令模式(command pattern)
在先前的讨论中,我们已经看到使用工厂类来返回指定操作符的对应的业务对象实例了,稍后,业务对象实例将用于之后的Claculator中执行计算操作。
我们也能够设计一个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类中定义一个用于接收操作和执行操作的新方法:
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 规则引擎(rule engine)
当我们最终编写了大量的嵌套if语句时,每一个条件都描述了特定的业务规则,用于评估正确逻辑操作的执行。规则引擎将这些复杂的草从主代码中去掉。规则引擎是用于评估规则并且基于输入返回结果。
让我们通过设计一个简单的规则引擎来做下试验。这个引擎是通过一组规则来处理表达式,并从选中的规则返回结果。首先,我们定义一个规则接口:
public interface Rule { boolean evaluate(Expression expression); Result getResult(); }
接着,我们来实现一个规则引擎:
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(); } }
这个规则引擎接收一个表达式并且返回Result。现在,我们设计一个带有两个数值变量以及一个用于操作的Operator对象的表达式(Expression)类:
public class Expression { private Integer x; private Integer y; private Operator operator; }
最后,我们定义一个AddRule类,它只在加操作中使用到:
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 Expression(5, 5, Operator.ADD); RuleEngine engine = new RuleEngine(); Result result = engine.process(expression); assertNotNull(result); assertEquals(10, result.getValue()); }
4. 总结
在这次教程中,我们探索了一系列不同的方法来简化复杂的代码。同时我们也学到了这么去使用有效的设计模式来取代繁杂的嵌套fi声明语句。
关注、转发、评论头条号每天分享java 知识,私信回复“555”赠送一些Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式资料
猜你喜欢
- 2024-10-09 JAVA超级基础之String型字符串(java string字符集)
- 2024-10-09 Java字符串相关面试题(java 字符串面试题)
- 2024-10-09 docx文档的模板替换及在线预览(word模板替换的方法)
- 2024-10-09 文本批量替换工具 正则式替换功能解决一切文本应用操作
- 2024-10-09 JAVA字符串多种相关操作总结(java中的字符串的实例方式有几种)
- 2024-10-09 String构造方法和字符集编码转换(string类构造方法)
- 2024-10-09 String、StringBuffer、StringBuilder的区别 #java
- 2024-10-09 Java中的IO流之字符流(java io 字符流)
- 2024-10-09 1807. 替换字符串中的括号内容(1807. 替换字符串中的括号内容是什么)
- 2024-10-09 Java中String、StringBuffer、StringBuilder的区别以及使用总结
你 发表评论:
欢迎- 最近发表
-
- 多种负载均衡算法及其Java代码实现
- 输入www.baidu.com背后经历了啥?说清楚这个,已经超过90%的人了
- 优化MySQL:为什么你应该用 UNSIGNED INT 存储IP地址
- 实模式下CPU如何获取数据及指令(实模式寻址方式)
- java基础都在这了,小主们拿去吧(java基础是指什么)
- 盘点爬虫语言为何选择Python而不是Java
- 搭载Dubbo+Zookeeper踩了这么多坑,我终于决定写下这篇
- 网络协议之TCP/IP协议(面试必考内容) - javaEE初阶 - 细节狂魔
- 深夜报警!10亿次请求暴击,如何用Redis找出最热IP?
- VPN技术(IPsec/L2TP/SSLVPN/PPTP)学习笔记
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)