锁定老帖子 主题:我的酒窝
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2006-12-16
场景一:
做stub。只实现一百个方法中的一个或者两个 场景二: 动态地仅仅intercept一个对象,而不是全局地针对一个类。 aspectj怎么做?既然就几行代码,写一下不是很容易? |
|
返回顶楼 | |
发表时间:2006-12-18
俺还是很喜欢ajoo的这个,好用极了...而且符合30-70原则(还没到20-80),呵呵
|
|
返回顶楼 | |
发表时间:2006-12-18
taowen 写道 引用 1。这个检查代价比较昂贵。基本上是现在要做的事情再多上一倍。 昂贵是指执行效率,占用空间,还是框架本身的代码行数?如果是框架作者的事情,那问题应该不大。做为一个feature排个序,有时间有必要就实现,没时间没必要就不实现。 引用 2。影响灵活性。比如你要是想从一个已有的类继承,然后用这个子类来动态实现接口,而基类里面已经有了额外多余的public method,怎么办?总不能跑到基类里面去加@Aux吧?或者现在基类没有多余方法,以后又突然增加了新的方法?我们一般作程序都有一个几乎是常识的假设:单单增加一个新的method,或者增加一个新的类,不会造成已有的代码失败。违反这个common sense有点怕怕。
那就区分对待好了。对于从Object继承下来的,明摆着就是一个同时实现一堆方法,但是又不想一次实现所有方法的语法糖。那就默认所有的方法都要改写要实现的接口的某个方法。对于从具体类集成下来的。那就不做检查。用@Override,让IDE给你检查好了。 引用 3。对stub的情况,假设方法名字写错了,一运行unit test马上就会发现错误了。在方法里设置个断点也是比较有效的方法。
是可以发现问题。只是加一点检查,可以fail fast。这和design by contract是一样的。没有那么多检查,代码该fail还是fail。只是多了一些检查,可以更快的失败,这样错误发生距离原因的距离就更近了一些。 引用 4。对在production code里面的动态implement,象场景二提到的那种,同样的,unit test必须要写的。而一旦有了unit test,错误也就会迅速体现出来了。 难道dimple不光是服务于测试的?在production里咋用? 引用 5。实现复杂度。需要实现一些额外的代码来做这件我还没拿定主意是否应当作或者怎么做最好的事。
实现复杂就先估计一下。列在TODO里。有时间再做。 引用 6。在dimple的外围做这个type checker也不难。就是判断一个类里面的所有public method签名在另一个接口里面都有定义。这个判断代码甚至不需要依赖dimple现有的代码。如果真需要,做个add-on好了。
最理想的是IDE的插件啦。Eclipse可以检查@Override,而且@Override的Retention Policy是Source级别的。如果有插件,也可以做到同样的便利程度。 引用 7。这个问题到底有多严重我还不是很清楚。如果用起来大家都觉得很担心把方法名字敲错,那么再想法子解决吧。ruby到处都是duck typing,也没看见大家很紧张的样子。
Ruby是Ruby嘛……到静态类型的语言中就该守静态类型的规矩,就应该利用静态类型带来的好处。在Ruby里还没法确定一个方法是不是存在于一个类中吧?只有调用了才知道(因为有method missing)。这就是Java的静态类型带来的束缚和好处。如果不利用,就浪费了。 增加了@Implement这个annotation。看看这个: http://docs.codehaus.org/display/DIMPLE/Dimple+Tips 如果一个类的方法都需要检查,就在类上用@Implement好了。 |
|
返回顶楼 | |
发表时间:2006-12-19
也增加了implementedBy和willImplement等函数来帮助在java 5以前的代码做fail fast。(为什么把类型检查设计成可选?因为不是所有时候这个检查都是需要的,有时候,一个method没有被用来实现接口里面的某个method是可以允许的)
|
|
返回顶楼 | |
发表时间:2006-12-20
ajoo 写道 场景一:
做stub。只实现一百个方法中的一个或者两个 场景二: 动态地仅仅intercept一个对象,而不是全局地针对一个类。 aspectj怎么做?既然就几行代码,写一下不是很容易? 针对场景一: public aspect AspectConnection { pointcut prepareStatement(String sql) : call(* java.sql.Connection.prepareStatement(String)) && args(sql); void around(String sql) : prepareStatement(sql) { //... 增加相应的处理 proceed(sql); //... 增加相应的处理 } } 对于场景二,我不太理解,为什么是指针对类的某些实例?这些实例用什么和其他实例加以区别?如果这个区别的方法比较简单的话,同样可以按照上面的做法处理。 当然,在项目中采用AspectJ会增加学习成本。具体用哪种方式更好,这要看开发人员对哪种技术比较熟悉。 |
|
返回顶楼 | |
发表时间:2006-12-20
floating 写道 ajoo 写道 场景一:
做stub。只实现一百个方法中的一个或者两个 场景二: 动态地仅仅intercept一个对象,而不是全局地针对一个类。 aspectj怎么做?既然就几行代码,写一下不是很容易? 针对场景一: public aspect AspectConnection { pointcut prepareStatement(String sql) : call(* java.sql.Connection.prepareStatement(String)) && args(sql); void around(String sql) : prepareStatement(sql) { //... 增加相应的处理 proceed(sql); //... 增加相应的处理 } } 对于场景二,我不太理解,为什么是指针对类的某些实例?这些实例用什么和其他实例加以区别?如果这个区别的方法比较简单的话,同样可以按照上面的做法处理。 当然,在项目中采用AspectJ会增加学习成本。具体用哪种方式更好,这要看开发人员对哪种技术比较熟悉。 没看懂这个aspect怎么实现的stub。写个客户程序的例子看看?前提是我测试的时候没有一个真正和数据库连接的Connection对象,只是想无中生有造出来一个假的作测试用。 场景二,比如说我拿到了一个Connection的实例,要把它传递给某一个不信任的函数someUntrustedFunction(myConnection)。我不希望这个函数关闭我的Connection实例。对此,我可以写: Connection nonCloseable = Implementor.proxy(Connection.class, new Object(){ public void close(){} }, myConnection); someUntrustedFunction(nonCloseable); 这是一个decorator模式的应用。 |
|
返回顶楼 | |
发表时间:2006-12-21
再举一个adapter的例子。
不知道大家遇到过这样的情况没有。有一个api,有这个接口签名: runApi(Map map){ ... map.get(...); ... } 它仅仅使用get()一个方法,但是因为种种原因,并没有使用最小接口(我认为这种设计是非常正常的),而是用了这个java.util.Map接口。 在使用这个api的时候,我们不幸的没有一个Map对象,而只有一个proprietary的PropertyMap(或者HttpServletRequest也行)对象: interface PropertyMap { String getProperty(String key); boolean containsKey(String key); Set keySet(); } 需要写个adapter吧?目标代码如下: PropertyMap pmap = ...; runApi(convertToMap(pmap)); 那么这个convertToMap怎么写呢? Map接口相当的肥厚阿。继承AbstractMap的话要实现entrySet(),我们也没有。而我们明明知道runApi()只需要一个能支持get()的Map就成了。 一个方法呢,就是用dimple,下面是一个pojo的adapter(不依赖dimple): public class PropertyMapAdapter { private final PropertyMap pmap; public Object get(Object key) { if(key instanceof String) { return pmap.get((String)key); } else return null; } public boolean containsKey(Object key) { if(key instanceof String) { return pmap.containsKey((String)key); } else return false; } public Set keySet() { return pmap.keySet(); } public boolean isEmpty() { return keySet().isEmpty(); } public int size() { return keySet().size(); } } 只需要实现我们关心的方法(一个安全的方法,可以显然PropertyMapAdapter implements Map,这样就可以用eclipse的代码生成来选择性地实现这些我们关心的方法,不会有typo。),不用管其它的乱七八糟的。 然后使用dimple,实现convertToMap如下: Map convertToMap(PropertyMap pmap) { return Implementor.proxy(Map.class, new PropertyMapAdapter(pmap)); } 搞定。 可见,dimple并不是仅仅可以用做aop的interceptor,它还是一个克服静态类型局限的OO利器亚。而且不象aspectj,相当的light-weight,没有什么侵入性。 |
|
返回顶楼 | |
发表时间:2006-12-21
场景一的情况:
客户程序就是很普通的调用啊。没有什么特别的地方。aspectj会织入到所有对prepareStatement调用的代码中。如果是源代码级别的,可以选择编译时织入。如果只有二进制代码,没有源码,那需要在classloading时候织入,这时候稍微复杂一些,需要告诉jvm使用aspectj的classloader。 场景二的情况: 只需要在例子上面的pointcut中对untrustFunction所在的类使用within关键字就可以了。代码片段如下: pointcut prepareStatement(String sql) : call(* java.sql.Connection.prepareStatement(String)) && args(sql) && within(ClassIncludeUntrustFunction); PreparedStatement around(String sql) : prepareStatement(sql) { //... PreparedStatement ret = proceed(sql); //.... return ret; } 说明一下,虽然用aspectJ可以方便地解决LZ提出的问题,但是我也挺喜欢LZ这类基于Dynamic Proxy方式的实现的。就像aspectJ将AOP的复杂性包装起来了一样,LZ的dimple也已经把使用Proxy的复杂性屏蔽的很好了。正如ajoo举的几个例子那样,dimple使用起来很简单,没有什么学习成本。基于上面的原因,我觉得我给出的方法更接近于启发一种解决问题的思路(比如,在没有找到dimple这个工具的情况下,呵呵)。 当然AOP技术发展的也很快,如果大家有精力,我还是建议大家关注一下aspectJ的。毕竟,AOP对开发程序的思维模式的影响是比较大的。 |
|
返回顶楼 | |
发表时间:2006-12-21
还是不对。aspectj的这个锤子和我的要求还是有差别的。
场景一:我是做个stub,不是要拦截所有的prepareStatement呀。easymock用过吧?一样的道理呀。莫非说得还不够明白?还是用aspectj的程序员从来不写单元测试的? 场景二:用within也不管用啊。我又不是想全局性拦截untrustedFunction。untrustedFunction被在若干个地方调用。而其它地方的代码在它close connection的时候都工作的很好,我并不清楚贸然禁止它调用Connection.close()到底是福是祸。我知道的是我的模块想要自己控制connection,所以需要局部地架空Connection.close。 拿起aspectj的锤子之前能不能先看看这个东西是不是钉子再说? |
|
返回顶楼 | |
发表时间:2006-12-22
ajoo 写道 还是不对。aspectj的这个锤子和我的要求还是有差别的。
场景一:我是做个stub,不是要拦截所有的prepareStatement呀。easymock用过吧?一样的道理呀。莫非说得还不够明白?还是用aspectj的程序员从来不写单元测试的? 我给出的场景一的代码并不是拦截了项目中所有的prepareStatement调用。通过修改aspectj的inpath属性可以指定拦截任意路径下的代码(比如所有单元测试代码所在路径)。 ajoo有些偏见了,AOP和单元测试是一点也不会发生互斥的两件事,谈不上用aspectj的程序员写不写单元测试(就像很多用普通的java的程序员也不写单元测试一样) ajoo 写道 场景二:用within也不管用啊。我又不是想全局性拦截untrustedFunction。untrustedFunction被在若干个地方调用。而其它地方的代码在它close connection的时候都工作的很好,我并不清楚贸然禁止它调用Connection.close()到底是福是祸。我知道的是我的模块想要自己控制connection,所以需要局部地架空Connection.close。 这个和上面的那个问题也可以用同样的方式解决。至于aspectj的适用范围的问题,我在上次的回帖中已经说了,aspectj已经将aop的复杂性屏蔽的很好了,需要补充的是,aop其实也挺适合用来做单元测试的工作的,至少是对它的一个有益的补充。具体的文章可以看看developerWorks上的相关文章。里面有几篇就是和单元测试相关的。 |
|
返回顶楼 | |