备注: 示例中的代码并不是真实代码的完全拷贝
无意出现的链式代码
还是前几天的事情,群里边有同事提到一个账单相关的需求,其中涉及到组装打印用数据。 有个新同事在Service里边写了一段这样的代码:
public XXService fillItem(String content, String tag) {
// some codes
this.printitems.add(new Item(tag, content));
return this;
}
有同学不是特别理解为什么要这么写,甚至发出return this是什么对象的疑问。
这段代码原来是这么写的:
public void fillItem(String content, String tag, List printitems) {
// some codes
printitems.add(new Item(tag, content));
}
有同学奇怪,为什么再提交一次printitems就会重新加一遍,认为第一段代码的问题出现在return this上面。 这样要说明一下,我们使用的是struts1,所以如果Service实例作为action的实例变量,那也是只有一个对象来的。 所以每次刷新一次重复加一遍就没什么奇怪的了。
回头来看那段新写的代码,出发点是好的。毕竟采用这种方式可以使用链式写法(如下所示),代码有时候会变得很sexy。
XXService.fillItem("a", "A").fillItem("b", "B");
这段代码的问题不在于是否采用链式写法,而是对这种单例,变量生命周期了解不足造成的。 链式写法在代码中很少会使用到,但在设计api,也是可以考虑的。设计的好,可以有效提高代码的编写效率和api的友好型。 例如,我就对java collection api里边的add方法深恶痛绝,就是不能连续add,要add多少个就要写多少行,还真的挺烦的。
采用链式写法的代码
在很多有名的开源框架中,链式写法也不是很少见,下面举几个例子:
很常见的有jquery
$("#name").attr("readonly", true).val("test");
还有mock框架mockito
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());
再看看rails的写法
Post.where('id > 10').limit(20).order('id desc').only(:order, :where)
Client.limit(5).offset(30)
我的看法
那么,如果你想采用链式写法,有什么地方需要注意呢? 1. 先写一下客户端代码,看调用方式合理么,符合sexy api么? 2. 采用链式操作的interface相关性比较强,经常一起出现。 3. 参数parameter一般较少,因为参数多了,代码很容易变得模糊不清。 4. 链式操作要么容错强(像jquery),要么就得直接抛出异常,对返回值不关心。
“improve bitter code”系列文章:
- 2012-07-21 improve bitter code
- 2012-07-24 improve bitter code: 迷惑的boolean参数
- 2012-07-24 improve bitter code: 更友好的链式写法
- 2012-07-28 improve bitter code: 看不懂的正则表达式
- 2012-07-29 improve bitter code: ‘不可避免’的重复
- 2012-07-29 improve bitter code: 拘泥于单出口方法
- 2012-07-31 improve bitter code: 对付魔鬼数字
- 2012-08-04 improve bitter code: 多掌握一门语言
- 2012-08-07 improve bitter code: 判空的处理
- 2012-08-15 improve bitter code: 没有行为的封装