重构:越来越长的 switch ... case 和 if ... else if ... else
2015-11-29 16:57
1036 查看
在代码中,时常有就一类型码(Type Code)而展开的如 switch ... case 或 if ... else if ... else 的条件表达式。随着项目业务逻辑的增加及代码经年累月的修改,这些条件判断逻辑往往变得越来越冗长。特别是当同样的逻辑判断出现在多个地方的时候(结构示意如下),代码的可读性和维护难易程度将变得非常的糟糕。每次修改时,你必须找到所有有逻辑分支的地方,并修改它们。
当代码中出现这样情况的时候,你就应考虑该重构它了(又有一种说法:要极力的重构 switch ... case 语句,避免它在你的代码中出现)。
重构掉类型码(Type Code)判断的条件表达式,我们需要引入面向对象的多态机制。第一种方式:使用子类来替换条件表达式。
我们以计算不同 role 的员工薪水的 Employee 类为例,一步步讲解重构的过程。原始待重构的类结构如下:
其中方法 getEmployeeSalary() 根据员工不同的角色返回当月薪水,当然,此处逻辑仅为示意,勿深究。
用子类方法重构后的类结构是这样的:
下面详细展开。首先,以类型码(Type Code)的宿主类 Employee 类为基类,为每个角色建立子类。
同时,将 Employee 基类的构造函数改造为静态 Create 工厂函数。
该静态工厂方法中也含有一 switch 逻辑。而重构后的代码中 switch 判断只有这一处,且只在对象创建的过程中涉及,不牵扯任何的业务逻辑,所以是可以接受的。
每个角色子类均覆写基类中的 getEmployeeSalary() 方法,应用自己的规则来计算薪水。
这样,基类 Employee 中的 getEmployeeSalary() 方法已无实际意义,将其变为 abstract 方法。
子类已经完全取代了臃肿的 switch 表达式。如果 Engineer, Salesman 或者 Manager 有新的行为,可在各自的子类中添加方法。或后续有新的 Employee Type, 完全可以通过添加新的子类来实现。在这里,通过多态实现了服务与其使用这的分离。
但是,在一些情况下,如对象类型在生命周期中需要变化(细化到本例,如 Engineer 晋升为 Manager)或者类型宿主类已经有子类,则使用子类重构的办法就无法奏效了。这时应用第二种方法:用 State/Strategy 来取代条件表达式。
同样,先上一张该方法重构后的类结构:
抽出一 EmployeeType 类,类型码的宿主类 Employee 对其进行引用。
EmployeeType 为基类,具体员工类型作为其子类。EmployeeType 类中含有一创建员工类型的静态工厂方法 Create。
将员工薪水的计算方法 getEmployeeSalary() 从 Employee 类迁徙到员工类型基类 EmployeeType 中。各具体员工类型类均 override 该方法。考虑到 EmployeeType 已无具体业务逻辑意义,将 EmployeeType 中的 getEmployeeSalary() 方法改为抽象方法。
最后,附上示意代码,点这里下载。
参考资料:
《重构 改善既有代码的设计》 Martin Fowler
switch(type) { case "1": ... break; case "2": ... break; case default: ... break; } ... ... ... ... switch(type) { case "1": ... break; case "2": ... break; case default: ... break; }
当代码中出现这样情况的时候,你就应考虑该重构它了(又有一种说法:要极力的重构 switch ... case 语句,避免它在你的代码中出现)。
重构掉类型码(Type Code)判断的条件表达式,我们需要引入面向对象的多态机制。第一种方式:使用子类来替换条件表达式。
我们以计算不同 role 的员工薪水的 Employee 类为例,一步步讲解重构的过程。原始待重构的类结构如下:
public class Employee_Ori { private int _type; private const int ENGINEER = 1; private const int SALESMAN = 2; private const int MANAGER = 3; private decimal _baseSalary = 10000; private decimal _royalty = 100; private decimal _bonus = 400; 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 = 10000; 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 = 10000; public override decimal getEmployeeSalary() { return _baseSalary; } }
最后,附上示意代码,点这里下载。
参考资料:
《重构 改善既有代码的设计》 Martin Fowler
相关文章推荐
- PHP 验证日期格式
- iOS之微博UI实例--拟物化设计(成功了90%)
- github上c++开源项目
- jquery.easing.js的使用示例
- 入手cocos2dx3.9笔记 2
- 对用户友好的设计
- PHP接收json,并将接收数据插入数据库
- 在eclipse中修改字体
- How to sort the dictionary by the value field
- C# string类常用的几种方法
- 杭电ACM1213(并查集)
- Android开发之Activity生命周期
- Android开发之Activity生命周期
- 扬州工业职业技术学院
- LeetCodeOJ——Word Search
- 信息安全系统设计基础第十二周学习总结
- 正则表达式(基础的东西)
- 绘制一个显示当前时间的StiilClock
- 关于share的小心得 ———李梦珂
- 15泛型_15.3泛型接口