学计算机的那个

不是我觉到、悟到,你给不了我,给了也拿不住;只有我觉到、悟到,才有可能做到,能做到的才是我的.

0%

多分支 if-else 重构

多分支 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;

//do somethig

如果有一系列条件测试都得到相同结果,将这些结果测试合并为一个条件表达式。

1
2
3
4
5
6
7
double disablityAmount(){
if(_seniority < 2 || _monthsDisabled > 12 || _isPartTime)
return 0;

//do somethig
}

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
/* 查找年龄大于18岁且为男性的学生列表 */
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
/* 查找年龄大于18岁且为男性的学生列表 */
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("嘉宾勋章");
}
}
//VIP勋章策略实现类
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];//type 为类型1,类型2,类型3

这个dict就是一个策略的集合,我们通过type来取出对应的结果.

题目:小明在每个星期每天都会做不同的事

  1.  周一打篮球
  2. 周二逛街
  3. 周三洗衣服
  4. 周四打游戏
  5. 周五唱歌
  6. 周六看电影
  7. 周末爬山

用分支来写,应该是这样的:

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
// 1.将复杂的业务逻辑包装成invocation,这里传入的每天做的事,例如playBasketball
- (NSInvocation *)invocationWithMethod:(SEL)selector{
NSMethodSignature*signature = [CurrentClass instanceMethodSignatureForSelector:@selector(selector)];
NSInvocation*invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = self;
invocation.selector = @selector(selector);
return invocation;
}
// 2.将每天做的事进行整合
- (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;
}
// 3.找出小明哪天做的事
NSInvocation *doWhat = self.Strategies[whichDay];
[doWhat invoke];

参考

  1. 6个实例详解如何把if-else代码重构成高质量代码

  2. if-else代码优化的八种方案

  3. [iOS]使用策略模式来去除繁琐的if-else