# 常见重构技巧 - 去除多余的if else

## 出现if/else和switch/case的场景

``````public int calculate(int a, int b, String operator) {
int result = Integer.MIN_VALUE;

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;
}
``````

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

## 重构思路

### 方式一 - 工厂类

• 定义一个操作接口
``````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("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);
}
``````

• Java 8 - 函数编程(lambda表达式)

• Lambda 表达式的特点?
• Lambda 表达式使用和Stream下的接口?
• 函数接口定义和使用，四大内置函数接口Consumer，Function，Supplier, Predicate.
• Comparator排序为例贯穿所有知识点。
• Java 8 - Optional类深度解析
• Optional类的意义?
• Optional类有哪些常用的方法?
• Optional举例贯穿所有知识点
• 如何解决多重类嵌套Null值判断?

### 方式二 - 枚举

• 定义操作符枚举
``````public enum Operator {
@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);
}
``````
• 写个测试用例测试下：
``````@Test
public void whenCalculateUsingEnumOperator_thenReturnCorrectResult() {
Calculator calculator = new Calculator();
int result = calculator.calculate(3, 4, Operator.valueOf("ADD"));
assertEquals(7, result);
}
``````

### 方法三 - 命令模式

• 首先让我们回顾下什么是命令模式

• 看这篇文章：行为型 - 命令模式(Command)

• 命令模式(Command pattern): 将"请求"封闭成对象, 以便使用不同的请求,队列或者日志来参数化其他对象. 命令模式也支持可撤销的操作。

• Command: 命令
• Invoker: 通过它来调用命令
• Client: 可以设置命令与命令的接收者

• Command接口

``````public interface Command {
Integer execute();
}
``````
• 实现Command
``````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();
}
``````
• 测试用例
``````@Test
public void whenCalculateUsingCommand_thenReturnCorrectResult() {
Calculator calculator = new Calculator();
int result = calculator.calculate(new AddCommand(3, 7));
assertEquals(10, result);
}
``````

• 创建型 - 简单工厂(Simple Factory)

• 简单工厂(Simple Factory)，它把实例化的操作单独放到一个类中，这个类就成为简单工厂类，让简单工厂类来决定应该用哪个具体子类来实例化，这样做能把客户类和具体子类的实现解耦，客户类不再需要知道有哪些子类以及应当实例化哪个子类

### 方法四 - 规则引擎

• 这里可以抽象出哪些类？// 头脑中需要有这种自动转化

• 规则Rule

• 规则接口
• 具体规则的泛化实现
• 表达式Expression
• 操作符
• 操作数
• 规则引擎
• 定义规则

``````public interface Rule {
boolean evaluate(Expression expression);
Result getResult();
}
``````
``````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;
}
}
``````
• 表达式
``````public class Expression {
private Integer x;
private Integer y;
private Operator operator;
}
``````
• 规则引擎
``````public class RuleEngine {
private static List<Rule> rules = new ArrayList<>();

static {
}

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();
}
}
``````
• 测试用例
``````@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());
}
``````

### 方法五 - 策略模式

• 什么是策略模式？

• 我们再复习下：行为型 - 策略(Strategy)
• 策略模式(strategy pattern): 定义了算法族, 分别封闭起来, 让它们之间可以互相替换, 此模式让算法的变化独立于使用算法的客户
• Strategy 接口定义了一个算法族，它们都具有 behavior() 方法。
• Context 是使用到该算法族的类，其中的 doSomething() 方法会调用 behavior()，setStrategy(in Strategy) 方法可以动态地改变 strategy 对象，也就是说能动态地改变 Context 所使用的算法。

• Spring中需要注入资源重构？

• 操作 // 很巧妙
``````public interface Opt {
int apply(int a, int b);
}

public class AddOpt implements Opt {
@Autowired
xxxAddResource resource; // 这里通过Spring框架注入了资源

@Override
public int apply(int a, int b) {
return resource.process(a, b);
}
}

@Component(value = "devideOpt")
public class devideOpt implements Opt {
@Autowired
xxxDivResource resource; // 这里通过Spring框架注入了资源

@Override
public int apply(int a, int b) {
return resource.process(a, b);
}
}
``````
• 策略
``````@Component
public class OptStrategyContext{

private Map<String, Opt> strategyMap = new ConcurrentHashMap<>();

@Autowired
public OptStrategyContext(Map<String, TalkService> strategyMap) {
this.strategyMap.clear();
this.strategyMap.putAll(strategyMap);
}

public int apply(Sting opt, int a, int b) {
return strategyMap.get(opt).apply(a, b);
}
}
``````

## 一些反思

• 真的要这么重构吗？

• 在实际开发中，切记`最怕的是刚学会成语，就什么地方都想用成语`; 很多时候不是考虑是否是最佳实现，而是折中（通常是业务和代价的折中，开发和维护的折中...），在适当的时候做适当的重构。
• 很多时候，让团队可持续性的维护代码便是最佳；
• 重构后会生成很多类，一个简单业务搞这么复杂？所以你需要权衡

## 常见重构技巧 - 5种方式去除多余的if else的更多相关文章

1. java 实现websocket的三种方式

Java中实现websocket常见有以下三种方式: 使用tomcat的websocket实现,需要tomcat 7.x,JEE7的支持. 使用spring的websocket,spring与webs ...

2. [翻译] C# 8.0 新特性 Redis基本使用及百亿数据量中的使用技巧分享（附视频地址及观看指南） 【由浅至深】redis 实现发布订阅的几种方式 .NET Core开发者的福音之玩转Redis的又一傻瓜式神器推荐

[翻译] C# 8.0 新特性 2018-11-13 17:04 by Rwing, 1179 阅读, 24 评论, 收藏, 编辑 原文: Building C# 8.0[译注:原文主标题如此,但内容 ...

3. 【http】四种常见的 POST 提交数据方式

来源:http://www.cnblogs.com/aaronjs/p/4165049.html HTTP/1.1 协议规定的 HTTP 请求方法有 OPTIONS.GET.HEAD.POST.PUT ...

4. 四种常见的 POST 提交数据方式

HTTP/1.1 协议规定的 HTTP 请求方法有 OPTIONS.GET.HEAD.POST.PUT.DELETE.TRACE.CONNECT 这几种.其中 POST 一般用来向服务端提交数据,本文 ...

5. 四种常见的 POST-------- content-type数据提交方式

HTTP/1.1 协议规定的 HTTP 请求方法有 OPTIONS.GET.HEAD.POST.PUT.DELETE.TRACE.CONNECT 这几种.其中 POST 一般用来向服务端提交数据,本文 ...

6. Web 四种常见的POST提交数据方式

HTTP/1.1 协议规定的 HTTP 请求方法有 OPTIONS.GET.HEAD.POST.PUT.DELETE.TRACE.CONNECT 这几种.其中 POST 一般用来向服务端提交数据,本文 ...

7. (转)四种常见的 POST 提交数据方式

四种常见的 POST 提交数据方式(转自:https://imququ.com/post/four-ways-to-post-data-in-http.html) HTTP/1.1 协议规定的 HTT ...

8. !!!四种常见的 POST 提交数据方式(含application/json)

HTTP/1.1 协议规定的 HTTP 请求方法有 OPTIONS.GET.HEAD.POST.PUT.DELETE.TRACE.CONNECT 这几种.其中 POST 一般用来向服务端提交数据,本文 ...

9. 技巧分享——如何去除多余的CSS代码？

有时候,当你的CSS代码过多的时候,而且已经明确知道有部分CSS代码是多余的: 这时候,有什么较快的办法可以去除多余的CSS呢?? 下面分享一个实用技巧: 1.使用谷歌浏览器:Chrome .下载 2 ...

10. （转载）四种常见的 POST 提交数据方式

(转载)http://www.imququ.com/post/four-ways-to-post-data-in-http.html HTTP/1.1 协议规定的 HTTP 请求方法有 OPTIONS ...

## 随机推荐

1. [摘录]第三部分 IBM文化(2)

第二十二章 原则性领导 在一个组织程序已经变得不受其来源和内容的约束,而且其编纂出来的组织宗旨已经代替了个人责任的组织之中,你所面临的首要任务,就是要全盘抹掉这个程序本身,以便让整个封闭的系统呼吸新鲜 ...

2. Sharepoint学习笔记—习题系列--70-576习题解析 -(Q59-Q62)

Question 59You are designing a collection of Web Parts that will be packaged into a SharePoint 2010 ...

3. U-Boot

4. Linux 安装 node

在 Linux 上安装 node,使用 Linux 编译后的版本最佳. 1.进入 node 官网,找到 Linux 版本下载,这里我们右键复制下载地址即可. 2.在 Linux 上,使用命令 curl ...

5. (转)HTML5实战与剖析之触摸事件(touchstart、touchmove和touchend)

HTML5中新添加了很多事件,但是由于他们的兼容问题不是很理想,应用实战性不是太强,所以在这里基本省略,咱们只分享应用广泛兼容不错的事件,日后随着兼容情况提升以后再陆续添加分享.今天为大家介绍的事件主 ...

6. Bloom Filter（布隆过滤器）

布隆过滤器用于测试某一元素是否存在于给定的集合中,是一种空间利用率很高的随机数据结构(probabilistic data structure),存在一定的误识别率(false positive),即 ...

7. Java中取某一个范围的随机数

一.取模操作 public static void main(String[] args) { for (int i = 1; i <= 20; i++) { int j = i % 11; S ...

8. Machine Learning 思维导图

Feature Enigneering 经用 sns.distplot 测试 一个Series 做 StandardScaler().fit_transform(Seriese) 之前和之后的图形形状 ...

9. C# webservice服务跟踪调试方法（转）

1.新建网站,添加服务,并创建服务. 2.打开internet 信息服务管理器,添加网站,映射到创建的服务所在网站的目录. 3.打开服务所在网站的解决方案,进行配置. 1) 设置启动选项 选择启动操作 ...

10. 绘图: matplotlib核心剖析

参考:http://www.cnblogs.com/vamei/archive/2013/01/30/2879700.html http://blog.csdn.net/ywjun0919/artic ...