前些日子问了透明一个天真的问题,透明亲自授道解惑后受宠若惊。SICP这本书我也买了好久了,只不过刚刚看到第二章。正所谓:书非借不能读也。其实我还准备问"那么是否可以把所有Stateful的过程形式化的转变为Stateless的过程",又觉得这个问题太大了,下面就谈一点自己这方面的体会。
再论语法糖这篇Blog的现实意义是什么呢?就是尽量避免写出Stateful的代码,因为Stateless的代码最不容易出错,便于测试,维护成本低,而且更加容易抽象。在实践中可以发现,大部分过程都可以是Stateless的。
- 每次声明一个Setter方法时都要拷问自己,是否会使对象变得Stateful,付出这样的代价是否值得,能否通过Constructor注入。如果对象创建以后没有这个属性,这个对象存在是否有意义。
其实Sping在这方面没有做出好榜样,他鼓励setter注入的方式注定了这个class是Stateful的。Martin Follower 也提过这个问题,鼓励大家通过Constructor注入。 - 再进一步,可以问问自己是否连Constructor都可以不要,直接在方法中传入。举个实际中的例子,一个分页类:
1)Stateful的分页类public class PageNavigator {
private int perPage; // 每页能够显示几项,通过实例变量避免重复传入这个值。
public PageNavigator(int num) {
perPage = num;
}
/**
* 返回第pageNum页的数据
* @param pageNum 第几页
* @param data 数据集
* @return 第pageNum页的数据
*/
public List getPage(int pageNum, DataSet data){
return data.getResultSet(pageNum*perPage, pageNum*(perPage+1));
}
}
2)另外一种实现就可以避免Stateful的出现public class PageNavigator {
/**
* 返回第pageNum页的数据
* @param pageNum 第几页
* @param data 数据集
* @param perPage 每页显示几行
* @return 第pageNum页的数据
*/
public List getPage(int pageNum, DataSet data, int perPage){
return data.getResultSet(pageNum*perPage, pageNum*(perPage+1));
}
}
那会有人问,如果采用第二种实现,每次调用都要perPage参数,如果每次perPage的值都一样,岂不是要重复很多次?很简单,再写一个方法就是了,比如每页显示20行的调用发生了许多次:private static final int PER_PAGE = 20;
public List getPage(int pageNum, DataSet data){
return getPage(pageNum,data, PER_PAGE);
}
再举一个实际的例子,Struts 的Action需要程序员考虑线程安全,而WebWork的不用,就是因为每次请求过程WebWork创建了一个新的Action实例,虽然这个实例不是Stateless的,但是对于每一次Web调用,它确实是Stateless的。