重构:越来越长的 switch ... case 和 if ... else if ... else
在代码中,时常有就一类型码(Type Code)而展开的如 switch ... case 或 if ... else if ... else 的条件表达式。随着项目业务逻辑的增加及代码经年累月的修改,这些条件判断逻辑往往变得越来越冗长。特别是当同样的逻辑判断出现在多个地方的时候(结构示意如下),代码的可读性和维护难易程度将变得非常的糟糕。每次修改时,你必须找到所有有逻辑分支的地方,并修改它们。
switch(type)
{
case "":
...
break;
case "":
...
break;
case default:
...
break;
} ... ...
... ... switch(type)
{
case "":
...
break;
case "":
...
break;
case default:
...
break;
}
当代码中出现这样情况的时候,你就应考虑该重构它了(又有一种说法:要极力的重构 switch ... case 语句,避免它在你的代码中出现)。
重构掉类型码(Type Code)判断的条件表达式,我们需要引入面向对象的多态机制。第一种方式:使用子类来替换条件表达式。
我们以计算不同 role 的员工薪水的 Employee 类为例,一步步讲解重构的过程。原始待重构的类结构如下:

public class Employee_Ori
{
private int _type; private const int ENGINEER = ;
private const int SALESMAN = ;
private const int MANAGER = ; private decimal _baseSalary = ;
private decimal _royalty = ;
private decimal _bonus = ; public Employee_Ori(int type)
{
this._type = type;
} public decimal getEmployeeSalary()
{
var monthlySalary = 0m; switch (this._type)
{
case ENGINEER:
monthlySalary = _baseSalary;
break;
case SALESMAN:
monthlySalary = _baseSalary + _royalty;
break;
case MANAGER:
monthlySalary = _baseSalary + _bonus;
break;
} return monthlySalary;
}
}
其中方法 getEmployeeSalary() 根据员工不同的角色返回当月薪水,当然,此处逻辑仅为示意,勿深究。
用子类方法重构后的类结构是这样的:

下面详细展开。首先,以类型码(Type Code)的宿主类 Employee 类为基类,为每个角色建立子类。
public class Engineer_Sub : Employee_Sub
{
...
} public class Salesman_Sub : Employee_Sub
{
...
} public class Manager_Sub : Employee_Sub
{
...
}
同时,将 Employee 基类的构造函数改造为静态 Create 工厂函数。
public static Employee_Sub Create(int type)
{
Employee_Sub employee = null; switch (type)
{
case ENGINEER:
employee = new Engineer_Sub();
break;
case SALESMAN:
employee = new Salesman_Sub();
break;
case MANAGER:
employee = new Manager_Sub();
break;
} return employee;
}
该静态工厂方法中也含有一 switch 逻辑。而重构后的代码中 switch 判断只有这一处,且只在对象创建的过程中涉及,不牵扯任何的业务逻辑,所以是可以接受的。
每个角色子类均覆写基类中的 getEmployeeSalary() 方法,应用自己的规则来计算薪水。
private decimal _baseSalary = ; public override decimal getEmployeeSalary()
{
return _baseSalary;
}
这样,基类 Employee 中的 getEmployeeSalary() 方法已无实际意义,将其变为 abstract 方法。
public abstract decimal getEmployeeSalary();
子类已经完全取代了臃肿的 switch 表达式。如果 Engineer, Salesman 或者 Manager 有新的行为,可在各自的子类中添加方法。或后续有新的 Employee Type, 完全可以通过添加新的子类来实现。在这里,通过多态实现了服务与其使用这的分离。
但是,在一些情况下,如对象类型在生命周期中需要变化(细化到本例,如 Engineer 晋升为 Manager)或者类型宿主类已经有子类,则使用子类重构的办法就无法奏效了。这时应用第二种方法:用 State/Strategy 来取代条件表达式。
同样,先上一张该方法重构后的类结构:

抽出一 EmployeeType 类,类型码的宿主类 Employee 对其进行引用。
public class Employee_State
{
// employee's type can be changed
private EmployeeType_State _employeeType = null;
}
EmployeeType 为基类,具体员工类型作为其子类。EmployeeType 类中含有一创建员工类型的静态工厂方法 Create。
public static EmployeeType_State Create(int type)
{
EmployeeType_State empType = null; switch (type)
{
case ENGINEER:
empType = new EngineerType_State();
break;
case SALESMAN:
empType = new SalesmanType_State();
break;
case MANAGER:
empType = new ManagerType_State();
break;
} return empType;
}
将员工薪水的计算方法 getEmployeeSalary() 从 Employee 类迁徙到员工类型基类 EmployeeType 中。各具体员工类型类均 override 该方法。考虑到 EmployeeType 已无具体业务逻辑意义,将 EmployeeType 中的 getEmployeeSalary() 方法改为抽象方法。
public class EngineerType_State : EmployeeType_State
{
private decimal _baseSalary = ; public override decimal getEmployeeSalary()
{
return _baseSalary;
}
}
最后,附上示意代码,点这里下载。
参考资料:
《重构 改善既有代码的设计》 Martin Fowler
重构:越来越长的 switch ... case 和 if ... else if ... else的更多相关文章
- C语言switch/case圈复杂度优化重构
软件重构是改善代码可读性.可扩展性.可维护性等目的的常见技术手段.圈复杂度作为一项软件质量度量指标,能从一定程度上反映这些内部质量需求(当然并不是全部),所以圈复杂度往往被很多项目采用作为软件质量的度 ...
- 使用策略模式重构switch case 代码
目录 1.背景 2.案例 3.switch…case…方式实现 4.switch…case…带来的问题 5.使用策略模式重构switch…case…代码 6.总结 1.背景 之前在看<重构 ...
- 重构if...else...或者switch程序块 为 中介者(Mediator)模式.的思考
http://www.cnblogs.com/insus/p/4142264.html 重构if...else...或者switch程序块 为 中介者(Mediator)模式.的思考 首先普世的编程架 ...
- Python | 基础系列 · Python为什么没有switch/case语句?
与我之前使用的所有语言都不同,Python没有switch/case语句.为了达到这种分支语句的效果,一般方法是使用字典映射: def numbers_to_strings(argument): sw ...
- C# 利用键值对取代Switch...Case语句
swich....case 条件分支多了之后,会严重的破坏程序的美观性. 比如这个 上述代码是用于两个进程之间通信的代码,由于通信的枚举特别的多,所以case的分支特别的多.导致了代码的可读性,可维护 ...
- 用Dictionary替换switch case
用switch case处理一个很长的判断,例如56个民族01代表汉族,02代表藏族,03代表壮族...,当传入数字想获取民族名称时就得写56个case,当传入民族获取背后的数字时,又得再写56个ca ...
- 使用反射+策略模式代替项目中大量的switch case判断
我这里的业务场景是根据消息类型将离线消息存入mongoDB不同的collection中.其中就涉及到大量的分支判断,为了增强代码的可读性和可维护性,对之前的代码进行了重构. 先对比一下使用反射+策略模 ...
- 如何优化代码中大量的if/else,switch/case?
前言 随着项目的迭代,代码中存在的分支判断可能会越来越多,当里面涉及到的逻辑比较复杂或者分支数量实在是多的难以维护的时候,我们就要考虑下,有办法能让这些代码变得更优雅吗? 正文 使用枚举 这里我们简单 ...
- asp.net ashx处理程序中switch case的替代方案总结
目录 1.用委托字典代替switch...case; 2.利用反射替代switch...case: 3.比较两种方案 4.其他方案 4.说明 5.参考 在开发 asp.net 项目中,通常使用一般处理 ...
随机推荐
- Web | JavaScript的提升机制
作用对象: 函数和变量的声明. 作用效果: 会将其声明提升到其所在的作用域的最顶端.函数会优先于变量的声明. //函数的提升优于变量的提升 test(); var a=2; function test ...
- 字体在mac win 系统如何优雅的展示
我们知道,不同的操作系统,不同的浏览器,页面字体的显示和渲染存在差异. 那么如何设置font-family,能够使字体在不同的环境下,也拥有好的展示效果? 1.操作系统中字体默认的字体 windows ...
- 『ACM C++』 PTA 天梯赛练习集L1 | 027-028
死亡周二,今天去看惊奇队长了!!!真的很佩服国外的后期特效大片技术,要是我们国内也能实现这样的技术能力就好了~ 羡慕max -------------------------------------- ...
- LL(1)文法--递归下降程序
递归下降程序 递归下降程序一般是针对某一个文法的.而递归下降的预测分析是为每一个非终结符号写一个分析过程,由于文法本身是递归的,所以这些过程也是递归的. 以上是前提. Sample 假如给的是正规式子 ...
- MySQL更新丢失
MySQL多主结构(比如: MGR Multi Master模式),如果多主都可以写的话,很有可能出现更新丢失的情况. 定义: T1时间,T2时间,T3时间 定义: 事务A, 事务B Node1节 ...
- 安装mysql时出现initialize specified but the data directory has files in in.Aborting.该如何解决
eclipse中写入sql插入语句时,navicat中显示的出现乱码(???). 在修改eclipse工作空间编码.navicate中的数据库编码.mysql中my.ini中的配置之后还是出现乱码. ...
- 【Linux】计划任务
计划任务的意义 计划任务 创建和管理在指定时间自动执行的任务 注意事项 要使任务计划在指定时间自动运行,计划任务的服务必须是启动的 计划任务分类 使用at命令调用atd进程设置在某个特定的时间,执行一 ...
- C# 数组集合分页 Skip Take
var input=new input(); var personList= new List<Person>(); //一个查询集合 var Total = personList.Cou ...
- laravel5.5源码笔记(六、中间件)
laravel中的中间件作为一个请求与响应的过滤器,主要分为两个功能. 1.在请求到达控制器层之前进行拦截与过滤,只有通过验证的请求才能到达controller层 2.或者是在controller中运 ...
- python学习笔记:第8天 文件操作
目录 1. 文件操作介绍 2. 文件操作的几种方式 3. 文件的操作的方法 1. 文件操作介绍 说到操作文件我们肯定会想到流,文件的操作都是通过流来操作的.在python中文件的操作非常简单,并不像J ...