多分支 if-else 重构
if-else不外乎两种场景:异常逻辑处理和不同状态处理
重构if-else时,心中无时无刻把握一个原则:
尽可能地维持正常流程代码在最外层。
意思是说,可以写if-else语句时一定要尽量保持主干代码是正常流程,避免嵌套过深。
实现的手段有:减少嵌套、移除临时变量、条件取反判断、合并条件表达式等
异常逻辑处理型重构
合并表达式
1 2 3 4 5 6 7 8 9 10 11 12
| double disablityAmount(){ if(_seniority < 2) return 0; if(_monthsDisabled > 12) return 0; if(_isPartTime) return 0;
|
如果有一系列条件测试都得到相同结果,将这些结果测试合并为一个条件表达式。
1 2 3 4 5 6 7
| double disablityAmount(){ if(_seniority < 2 || _monthsDisabled > 12 || _isPartTime) return 0; }
|
1 2 3 4 5 6 7 8 9 10
| public double getAdjustedCapital(){ double result = 0.0; if(_capital > 0.0 ){ if(_intRate > 0 && _duration >0){ resutl = (_income / _duration) *ADJ_FACTOR; } } return result; }
|
1 2 3 4 5 6 7 8 9 10
| public double getAdjustedCapital(){ if(_capital <= 0.0 ){ return 0.0; } if(_intRate > 0 && _duration >0){ return (_income / _duration) *ADJ_FACTOR; } return 0.0; }
|
这样重构后,还不够,因为主要的语句(_income / _duration) *ADJ_FACTOR;
在if内部,并非在最外层,根据优化原则(尽可能地维持正常流程代码在最外层),可以再继续重构:
1 2 3 4 5 6 7 8 9 10 11
| public double getAdjustedCapital(){ if(_capital <= 0.0 ){ return 0.0; } if(_intRate <= 0 || _duration <= 0){ return 0.0; } return (_income / _duration) *ADJ_FACTOR; }
|
这才是好的代码风格,逻辑清晰,一目了然,没有if-else嵌套难以理解的流程。
这里用到的重构方法是:将条件反转使异常情况先退出,让正常流程维持在主干流程
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public ArrayList<Student> getStudents(int uid){ ArrayList<Student> result = new ArrayList<Student>(); Student stu = getStudentByUid(uid); if (stu != null) { Teacher teacher = stu.getTeacher(); if(teacher != null){ ArrayList<Student> students = teacher.getStudents(); if(students != null){ for(Student student : students){ if(student.getAge() > = 18 && student.getGender() == MALE){ result.add(student); } } }else { logger.error("获取学生列表失败"); } }else { logger.error("获取老师信息失败"); } } else { logger.error("获取学生信息失败"); } return result; }
|
典型的”箭头型”代码,最大的问题是嵌套过深,解决方法是异常条件先退出,保持主干流程是核心流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public ArrayList<Student> getStudents(int uid){ ArrayList<Student> result = new ArrayList<Student>(); Student stu = getStudentByUid(uid); if (stu == null) { logger.error("获取学生信息失败"); return result; } Teacher teacher = stu.getTeacher(); if(teacher == null){ logger.error("获取老师信息失败"); return result; } ArrayList<Student> students = teacher.getStudents(); if(students == null){ logger.error("获取学生列表失败"); return result; } for(Student student : students){ if(student.getAge() > 18 && student.getGender() == MALE){ result.add(student); } } return result; }
|
状态处理型重构
公共函数封装
1 2 3 4 5 6 7 8 9 10 11 12 13
| double getPayAmount(){ Object obj = getObj(); double money = 0; if (obj.getType == 1) { ObjectA objA = obj.getObjectA(); money = objA.getMoney()*obj.getNormalMoneryA(); } else if (obj.getType == 2) { ObjectB objB = obj.getObjectB(); money = objB.getMoney()*obj.getNormalMoneryB()+1000; } }
|
重构后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| double getPayAmount(){ Object obj = getObj(); if (obj.getType == 1) { return getType1Money(obj); } else if (obj.getType == 2) { return getType2Money(obj); } } double getType1Money(Object obj){ ObjectA objA = obj.getObjectA(); return objA.getMoney()*obj.getNormalMoneryA(); } double getType2Money(Object obj){ ObjectB objB = obj.getObjectB(); return objB.getMoney()*obj.getNormalMoneryB()+1000; }
|
重构方法是:把if-else内的代码都封装成一个公共函数。函数的好处是屏蔽内部实现,缩短if-else分支的代码。代码结构和逻辑上清晰,能一下看出来每一个条件内做的功能
。
用多态取代条件表达式
1 2 3 4 5 6 7 8 9 10
| double getSpeed(){ switch(_type){ case EUROPEAN: return getBaseSpeed(); case AFRICAN: return getBaseSpeed()-getLoadFactor()*_numberOfCoconuts; case NORWEGIAN_BLUE: return (_isNailed)?0:getBaseSpeed(_voltage); } }
|
重构后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class Bird{ abstract double getSpeed(); } class European extends Bird{ double getSpeed(){ return getBaseSpeed(); } } class African extends Bird{ double getSpeed(){ return getBaseSpeed()-getLoadFactor()*_numberOfCoconuts; } } class NorwegianBlue extends Bird{ double getSpeed(){ return (_isNailed)?0:getBaseSpeed(_voltage); } }
|
策略模式+工厂方法消除 if else
假设需求为,根据不同勋章类型,处理相对应的勋章服务,优化前有以下代码:
1 2 3 4 5 6 7 8 9
| String medalType = "guest"; if ("guest".equals(medalType)) { System.out.println("嘉宾勋章"); } else if ("vip".equals(medalType)) { System.out.println("会员勋章"); } else if ("guard".equals(medalType)) { System.out.println("展示守护勋章"); } ...
|
首先,我们把每个条件逻辑代码块,抽象成一个公共的接口,可以得出以下代码:
1 2 3 4
| public interface IMedalService { void showMedal(); }
|
我们根据每个逻辑条件,定义相对应的策略实现类,可得以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class GuardMedalServiceImpl implements IMedalService { @Override public void showMedal() { System.out.println("展示守护勋章"); } }
public class GuestMedalServiceImpl implements IMedalService { @Override public void showMedal() { System.out.println("嘉宾勋章"); } }
public class VipMedalServiceImpl implements IMedalService { @Override public void showMedal() { System.out.println("会员勋章"); } }
|
接下来,我们再定义策略工厂类,用来管理这些勋章实现策略类,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class MedalServicesFactory {
private static final Map<String, IMedalService> map = new HashMap<>(); static { map.put("guard", new GuardMedalServiceImpl()); map.put("vip", new VipMedalServiceImpl()); map.put("guest", new GuestMedalServiceImpl()); } public static IMedalService getMedalService(String medalType) { return map.get(medalType); } }
|
使用了策略+工厂模式之后,代码变得简洁多了,如下:
1 2 3 4 5 6 7 8
| public class Test { public static void main(String[] args) { String medalType = "guest"; IMedalService medalService = MedalServicesFactory.getMedalService(medalType); medalService.showMedal(); } }
|
[iOS]使用策略模式来去除繁琐的if-else
通过传入的三种类型来获取对应的id,例如id 001->类型1,id 002->类型2,id 003->类型3
1 2 3 4 5 6 7
| if (type == 类型1) { return 001; }else if (type == 类型2){ return 002; }else if (type == 类型3){ return 003; }
|
用策略模式如何实现呢?
1 2 3 4 5 6 7
| NSDictionary *dict =@{ @"类型1" : @001, @"类型2" : @002, @"类型3" : @003 }; NSInteger id = dict[type];
|
这个dict就是一个策略的集合,我们通过type来取出对应的结果.
题目:小明在每个星期每天都会做不同的事
- 周一打篮球
- 周二逛街
- 周三洗衣服
- 周四打游戏
- 周五唱歌
- 周六看电影
- 周末爬山
用分支来写,应该是这样的:
1 2 3 4 5 6 7 8 9 10
| if(day == 周一){ result = [xiaoming playBasketball]; }else if (day == 周二){ result = [xiaoming shopping]; }else if (day == 周三){ result = [xiaoming washClothes]; }else if(...) { ... } NSLog(@"xiaoming 今天%@",result);
|
策略优化后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| - (NSInvocation *)invocationWithMethod:(SEL)selector{ NSMethodSignature*signature = [CurrentClass instanceMethodSignatureForSelector:@selector(selector)]; NSInvocation*invocation = [NSInvocation invocationWithMethodSignature:signature]; invocation.target = self; invocation.selector = @selector(selector); return invocation; }
- (NSDictionary *)Strategies{ NSDictionary *Strategies = @{ @"day1" : [self invocationWithMethod:@selector(playBasketball)], @"day2" : [self invocationWithMethod:@selector(shopping)], @"day3" : [self invocationWithMethod:@selector(washClothes)], @"day4" : [self invocationWithMethod:@selector(playGames)], @"day5" : [self invocationWithMethod:@selector(sing)], @"day6" : [self invocationWithMethod:@selector(watchMovie)], @"day7" : [self invocationWithMethod:@selector(climbing)], }; return Strategies; }
NSInvocation *doWhat = self.Strategies[whichDay]; [doWhat invoke];
|
参考
6个实例详解如何把if-else代码重构成高质量代码
if-else代码优化的八种方案
[iOS]使用策略模式来去除繁琐的if-else