如何在被调函数中控制调用函数返回

有些时候,一个方法需要满足一些条件才能执行。所以,在真正产生实际作用的代码前,需要进行很多判断,判断初始条件是否满足。这样的方法可能长这个样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def some_method
if condiation_a
return "it doesn't satisfy condiation a"
elsif condition_b
return "it doesn't satisfy condition b"
elsif condition_c
if condition_d
return "it doesn't satisfy condition d"
end
else
if condition_e
return "it doesn't satisfy condition e"
end
end
do_something
end

对于第一次阅读这个方法的开发者,最有意义的应该是最后一行,但前面的判断条件却占据了这个方法的大部分空间。如果一个阅读者是顺序阅读,那么他的脑力在读到真正起关键作用的代码之前,可能已经快要枯竭了。毕竟,人的短期记忆只能记住(并可以应用) 3 或 4 条信息,多一点的话也就 7 条。前面那么一大长串树形结构的判断条件,反而可能阻碍其他开发者去阅读。

怎么办?

能不能把那些判断条件封装成另外一个方法?这好像是个不错的选择,但是那些判断条件都在控制some_method的返回,如果抽象到另外一个方法中,比如judge方法中,那么judge方法中 return 是无法控制some_method的返回的。

利用被调函数的返回值,控制调用函数的返回

如果我们把那些判断条件抽象出去的时候,在调用函数中增加一个条件来判断封装好的方法的返回值,就能决定调用函数是否需要返回了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def some_method
result = judge
return result if result
do_something
end

def judge
if condiation_a
return 'it doesnt satisfy condiation a'
elsif condition_b
return 'it doesnt satisfy condition b'
elsif condition_c
if condition_d
return 'it doesnt satisfy condition d'
end
else
if condition_e
return 'it doesnt satisfy condition e'
end
end
end

这样的重构是不是让 some_method 看起来清爽多了。

如果你不关心judge方法返回什么样的错误信息,你甚至可以将some_method方法写成这样:

1
2
3
4
def some_method
judge and return
do_something
end

在上面这个重构的方法中,如果 judge 方法的返回值不是 nil,那么some_method就会被返回。据说这种带有 a_judgement and return 的结构在写 ruby 和 rails 代码时很常用。

利用被调函数抛出的异常,控制调用函数的返回

根据调用栈的特点,如果被调函数抛出异常,他就会从调用栈中弹出,这个时候,如果在调用函数中增加异常的捕捉,程序的执行就有回到了调用函数上,而且还捕捉到了异常信息。所以,我们可以这么重构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def some_method
judge
do_something
rescue ArgumentError => ex
ex.message
end

def judge
error_msg =
if condiation_a
'it doesnt satisfy condiation a'
elsif condition_b
'it doesnt satisfy condition b'
elsif condition_c
if condition_d
'it doesnt satisfy condition d'
end
else
if condition_e
'it doesnt satisfy condition e'
end
end
raise ArgumentError, error_msg
end

这样的重构中,some_method也是比较清爽的。