显示标签为“重构”的博文。显示所有博文
显示标签为“重构”的博文。显示所有博文

2009年3月24日星期二

把条件判断提到方法外

我在开发中见过这样的方法:
function doSomething(flag, arg1, arg2) {
if !flag return;
// doSomething logic
....
}

那我就奇怪了,既然caller了解flag,为何不先判断,符合条件才进入doSomething方法呢?

你可能会说我不会写出这样的代码。那看看下面这种稍复杂点儿的变体:
function doSomething(arg1, arg2) {
flag = arg1.checkSomething(arg2);
if !flag return;
// doSomething logic
...
}

根据传入参数的计算结果来决定是否执行后续逻辑的情况与第一个例子其实是一样的。

有时候出现这种情况可能是代码在修改的过程中没有意识到这个问题。另外一种可能的理由是doSomething被多处地方调用,为了避免重复条件判断逻辑,所以把条件判断下推到doSomething方法。

这个理由在某些情况下可能是合理的,比如调用地方太多,改动起来会影响到很多测试,而当前没有足够时间。

但一般来说,我认为对这样的情况,减少条件判断的重复的好处不足以弥补代码在表达性上的损失,而且还要加上doSomething方法的测试所增加的复杂度的代价。

2009年2月11日星期三

Inline parameter

Derek同学的启发,我想把我们在实践中总结下来的一些重构小技巧记录下来。

我们自己在实际中经常用到Extract Parameter这个重构功能,感觉非常方便。但可惜的是没有这个功能的反向功能。比如说一个方法接受三个参数,但检查这个方法所有的调用处发现,第三个参数完全可以由前两个参数(或者其他可视范围内的值)计算得出。这时你就需要一种重构来把这个参数的计算过程推到方法内部,然后删除这个参数。

遗憾的是IntelliJ不提供这样的自动化重构功能。我们可以手动来做,但如果代码很多,就有风险。怎么尽量降低这种风险呢?

我想到了一种方法。假设目标方法为foo,我们想要删除的参数为bar。步骤如下:
1. 对计算bar的表达式提取方法(IntelliJ能自动检测到相同代码片段并提示自动替换)
2. 在方法foo的所有调用处,把参数bar替换为对新方法的调用
3. 修改foo方法的代码,在第一行将bar赋值为对新方法调用的返回值
3. (在foo方法内,)内联bar(,然后bar在foo方法内部已没有引用)
4. 修改foo方法的签名,安全删除bar(所有调用处用于生成bar的表达式即自动删除)