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月26日星期四

切换hg分支

用hg已有多日,下面的这个法子是前段时间摸索出来的偷懒招数。

问题是这样:项目中有两个代码库分支:trunk和1.2。每个代码库都超过500M。我的C盘剩余空间已经不多,没法同时保留两个分支的本地副本。平时我主要在trunk上工作,有时要修1.2的bug就得把分支切换到1.2上。重新checkout整个代码库很慢,怎么办呢?

既然是分支,那它们肯定在某一点之前共享相同的历史。所以我只需要先回溯到这个基点,再获取1.2分支上自从这一点后所有的历史即可!

具体做法:

1. 执行hg out ${url_of_1.2},得到trunk分支上与1.2不同的最早的节点,记为revision1
2. 执行hg strip revision1,删除以revision1为根的所有版本
3. 重复步骤1,检查是否还有1.2分支上不存在的版本;如有,则重复步骤3
4. 至此,本地副本上的所有版本都是1.2上的版本;这时可以执行hg pull ${url_of_1.2}获取1.2上的新的版本
5. 大功告成,你已经得到一个与中心库上1.2分支完全一致的代码库

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的表达式即自动删除)

2009年1月22日星期四

Fix Google Reader显示问题

早就觉得Google Reader的段落排版有问题了。字体太小,排版紧凑,对眼睛很不友好!

以往每次看文章,都得敲两次=,放大字体,但还是解决不了间距的问题。密密麻麻的,看着不舒服。

今天终于用firefox的user css解决了这个问题。

只需把下面这段代码放到firefox的profile\chrome下的userContent.css里:

@-moz-document url-prefix(http://www.google.com/reader/view/) {
.entry-body { font-size: 1.4em; line-height: 1.5em; }
}

Firefox3的profile目录在Windows下是:%APPDATA%\Mozilla\Firefox\Profiles\xxx.default

参考:Site specific stylesheet in Mozilla

2009年1月3日星期六

Parallelize your tests with Test-Load-Balancer

With the fact that Cruise runs jobs in parallel to make long running test suites go faster, you can simply split your long running test suite into multiple suites and create jobs for each one. Given enough build agents, Cruise can run all jobs within a specified stage simultaneously, which means your long running testing can be done in a snap.

But wait, why do I need to manually split my test suites?

For example, I have a unit-test target in my ant build script which include more than 100 tests. Those tests are organized by corresponding packages, so it's hard to split them in an easy way. It's a burden to most of Cruise users.

That's why we have Test-Load-Balancer now.

What is test-load-balancer?

TLB(Test-Load-Balancer) is a tool that can split test suites for you in Cruise. With TLB, you can simply specify how many pieces you want to split for a specific job using an easy, intuitive way. TLB can automatically work out how many pieces and which piece that current job should do. For example, you have a unit test job running on Linux environment named 'ut-linux', it has 100 tests and costs 10 minutes. Now you want to split it to three jobs, what you need to do is just replace the old 'ut-linux' job with three jobs: 'ut-linux-1', 'ut-linux-2', 'ut-linux-3'. Then you can run unit tests on Linux in three pieces simultaneously, and get benefits of reducing time cost to one third of the old cost.

How to use it?

First, download test-load-balancer jar file and add it to the classpath of ant build script.

Then, change your build.xml as following:

<typedef name="filter-fileset" classname="com.googlecode.tlb.support.junit.FilterFileSet" classpathref="classpath"/>

<junit>
<batchtest todir="target/test-results">
<filter-fileset dir="target/test-classes" includes="**/*Test.class"/>
</batchtest>
<classpath refid="classpath" />
</junit>

Third, change your Cruise configuration to use TLB style jobs such as ut-linux-1, ut-linux-2, ut-linux-3 ...

Then you can get the benefits of faster run.

Further information about test-load-balancer.

A video demonstration about TLB: