一、重构If-Else语句(一)
前提:
- if-else 代码是每一个程序员最容易写出的代码,同时也是最容易被写烂的代码,稍不注意,就产生一堆难以维护和逻辑混乱的代码。
- 随着业务的瞬息变化,需要对入参的类型和值进行判断;
- 判断下 对象/值 是否为null
- 不同类型/状态,执行不同的流程
- 落地到具体实现只能不停地加 if-else 来处理,渐渐地,代码变得越来越庞大,函数越来越长,文件行数也迅速突破上千行,维护难度也越来越大,到后期基本达到一种难以维护的状态。
1、 if-else 代码缺点?
- 缺点:代码逻辑复杂,维护性差,极容易引发 bug。
2、优化方案,如何重构?
- 重构 if-else 时,遵循一个原则:
- 维持正常流程代码在最外层
- 编写 if-else 语句时保持主干代码是正常流程,避免嵌套过深
- 实现的手段:减少嵌套、移除临时变量、条件取反判断、合并条件表达式、多态重构
3、重构处理类型:
(1)异常逻辑处理型
①、合并条件表达式:若有一系列条件测试都得到相同结果,将这些结果测试合并为一个条件表达式
-
重构前:
-
1 double disablityAmount(){ 2 if(_seniority < 2) 3 return 0; 4 5 if(_monthsDisabled > 12) 6 return 0; 7 8 if(_isPartTime) 9 return 0; 10 11 //do somethig 12 }
-
-
重构后:
-
1 double disablityAmount(){ 2 if(_seniority < 2 || _monthsDisabled > 12 || _isPartTime) 3 return 0; 4 5 //do somethig 6 }
-
②、减少 if-else 嵌套:减少嵌套、移除临时变量(嵌套内的 if-else 和最外层没有关联性)
-
重构前:
-
1 double getPayAmount(){ 2 double result; 3 if(_isDead) { 4 result = deadAmount(); 5 }else{ 6 if(_isSeparated){ 7 result = separatedAmount(); 8 } 9 else{ 10 if(_isRetired){ 11 result = retiredAmount(); 12 else{ 13 result = normalPayAmount(); 14 } 15 } 16 } 17 return result; 18 }
-
-
重构后:
-
1double getPayAmount(){ 2 if(_isDead) 3 return deadAmount(); 4 5 if(_isSeparated) 6 return separatedAmount(); 7 8 if(_isRetired) 9 return retiredAmount(); 10 11 return normalPayAmount(); 12}
-
③、条件取反:减少嵌套、移除临时变量、维持正常流程代码在最外层(将条件反转使异常情况先退出,让正常流程维持在主干流程)
-
重构前:
-
1 public double getAdjustedCapital(){ 2 double result = 0.0; 3 if(_capital > 0.0 ){ 4 if(_intRate > 0 && _duration >0){ 5 resutl = (_income / _duration) *ADJ_FACTOR; 6 } 7 } 8 return result; 9 }
-
-
重构后:
-
1 public double getAdjustedCapital(){ 2 if(_capital <= 0.0 || _intRate <= 0 || _duration <= 0){ 3 return 0.0; 4 } 5 return (_income / _duration) *ADJ_FACTOR; 6 }
-
④、"箭头型"代码,减少嵌套:嵌套过深,解决方法是异常条件先退出,保持主干流程是核心流程
-
重构前:
-
1 /* 查找年龄大于18岁且为男性的学生列表 */ 2 public ArrayList<Student> getStudents(int uid){ 3 ArrayList<Student> result = new ArrayList<Student>(); 4 Student stu = getStudentByUid(uid); 5 if (stu != null) { 6 Teacher teacher = stu.getTeacher(); 7 if(teacher != null){ 8 ArrayList<Student> students = teacher.getStudents(); 9 if(students != null){ 10 for(Student student : students){ 11 if(student.getAge() > = 18 && student.getGender() == MALE){ 12 result.add(student); 13 } 14 } 15 }else { 16 logger.error("获取学生列表失败"); 17 } 18 }else { 19 logger.error("获取老师信息失败"); 20 } 21 } else { 22 logger.error("获取学生信息失败"); 23 } 24 return result; 25 }
-
-
重构后:
-
1 /* 查找年龄大于18岁且为男性的学生列表 */ 2 public ArrayList<Student> getStudents(int uid){ 3 ArrayList<Student> result = new ArrayList<Student>(); 4 Student stu = getStudentByUid(uid); 5 if (stu == null) { 6 logger.error("获取学生信息失败"); 7 return result; 8 } 9 10 Teacher teacher = stu.getTeacher(); 11 if(teacher == null){ 12 logger.error("获取老师信息失败"); 13 return result; 14 } 15 16 ArrayList<Student> students = teacher.getStudents(); 17 if(CollectionUtils.isNotEmpty(students){ 18 logger.error("获取学生列表失败"); 19 return result; 20 } 21 22 for(Student student : students){ 23 if(student.getAge() > 18 && student.getGender() == MALE){ 24 result.add(student); 25 } 26 } 27 return result; 28 }
-
(2)状态处理型
①、这里使用的重构方法是:把 if-else 内的代码都封装成一个公共函数。函数的好处是屏蔽内部实现,缩短 if-else 分支的代码。代码结构和逻辑上清晰,能一下看出来每一个条件内做的功能。
-
重构前:
-
1 double getPayAmount(){ 2 Object obj = getObj(); 3 double money = 0; 4 if (obj.getType == 1) { 5 ObjectA objA = obj.getObjectA(); 6 money = objA.getMoney()*obj.getNormalMoneryA(); 7 } 8 else if (obj.getType == 2) { 9 ObjectB objB = obj.getObjectB(); 10 money = objB.getMoney()*obj.getNormalMoneryB()+1000; 11 } 12 }
-
-
重构后
-
1 double getPayAmount(){ 2 Object obj = getObj(); 3 if (obj.getType == 1) { 4 return getType1Money(obj); 5 } 6 else if (obj.getType == 2) { 7 return getType2Money(obj); 8 } 9 } 10 11 double getType1Money(Object obj){ 12 ObjectA objA = obj.getObjectA(); 13 return objA.getMoney()*obj.getNormalMoneryA(); 14 } 15 16 double getType2Money(Object obj){ 17 ObjectB objB = obj.getObjectB(); 18 return objB.getMoney()*obj.getNormalMoneryB()+1000; 19 }
-
②、多态取代表达式(推荐):根据对象类型的不同而选择不同的行为,这个表达式的每个分支放进一个子类内的覆写函数中,然后将原始函数声明为抽象函数。但使用多态对原来代码修改过大,最好在设计之初就使用多态方式
-
重构前:
-
1 double getSpeed(){ 2 switch(_type){ 3 case EUROPEAN: 4 return getBaseSpeed(); 5 case AFRICAN: 6 return getBaseSpeed()-getLoadFactor()*_numberOfCoconuts; 7 case NORWEGIAN_BLUE: 8 return (_isNailed)?0:getBaseSpeed(_voltage); 9 } 10 }
-
-
重构后:
-
1 class Bird{ 2 abstract double getSpeed(); 3 } 4 5 class European extends Bird{ 6 double getSpeed(){ 7 return getBaseSpeed(); 8 } 9 } 10 11 class African extends Bird{ 12 double getSpeed(){ 13 return getBaseSpeed()-getLoadFactor()*_numberOfCoconuts; 14 } 15 } 16 17 class NorwegianBlue extends Bird{ 18 double getSpeed(){ 19 return (_isNailed)?0:getBaseSpeed(_voltage); 20 } 21 }
-
总结:
1、if-else 代码是每一个程序员最容易写出的代码,同时也是最容易被写烂的代码,稍不注意,就产生一堆难以维护和逻辑混乱的代码。
- 针对条件型代码重构把握一个原则:
- 维持主干流程代码在最外层
- 合并条件表达式可以有效地减少if语句数目
- 减少嵌套能减少深层次逻辑
- 异常条件先退出自然而然主干流程就是正常流程。
- 针对状态处理型重构方法有两种:
- 把不同状态的操作封装成函数,简短 if-else 内代码行数;
- 利用面向对象多态特性直接干掉了条件判断。