- 浏览: 452731 次
最新评论
-
carlos23:
我想知道用WriterLogger后,里面的writer在外面 ...
论面向组合子程序设计方法 之 燃烧的荆棘 -
marsyoung:
讲的什么。。没看懂。例子太抽象。。
论面向组合子程序设计方法 之 创世纪 -
wanghhao:
优雅的方式!
论面向组合子程序设计方法 之 微步毂纹生 -
ykdsg:
有源码吗?
论面向组合子程序设计方法 之 monad -
咱不怕:
如果在一个函数中多次使用到这个不变temp对象,按照Marti ...
关于 Replace Temp With Query
场景一:
个人喜欢state-based的单元测试。(定义见mock is not stub)。
可是有些时候,比如在测试一些使用java.sql.Connection, ibatis SqlMapClient等接口的类的时候,明显写stub很不好。(1,接口很大,有很多不相干的东西。2,版本一旦变化,这些接口可能跟着变化,如果写stub的话,就意味着stub要跟着这些第三方接口变化)
于是,只好mock。只好interaction based。只好每回内部实现一变就死盯着一坨坨的expectAndReturn找不再有效的expectation。
场景二:
一个遗留系统用自己的连接池,使用ConnectionManager.checkIn(Connection)来释放连接(而不是Connection.close)
为了重构,希望能够重载Connection.close(),让它调用checkIn()。
跟场景一的提到的原因一样,不希望直接写一个MyConnection implements Connection。于是只好自己写dynamic proxy,形成如下的代码:
后面又发现这个变态的一流系统居然用自己的startMyTransaction, endMyTransaction(), rollbackMyTransaction()来搞事务处理!
于是又要继续判断method.getName().equals("startTransaction"), method.getName().equals("commitTransaction")等等等等。
dynamic proxy的代码难看的要死。
最近终于受不了了。愤然写了一个叫做酒窝的东西(dimple)。
所谓dimple,就是dynamic implementor的意思。我要用纯java语法来实现我想实现的方法(不是expectAndReturn,也不是if(method.getName().equals("method1"))),但是同时我又不愿意写“implements TheInterface”,以避免被迫处理那些我不关心的method。
用法如下:
场景一:
这样实现我的stub:
没有讨厌的expectAndReturn,不用担心parameter matcher。
场景二:
于是,对close(), startTransaction, commitTransaction(),我们都直接调用ConnectionManager, 而对其它的method则委托给realConn。
这个Implementor类是纯java代码,除了dynamic proxy没有用任何其它技术。(没有enhancement,aop之类的)
个人感觉还是挺有用的。大家点评一下?
我不是说:一般情况下你应该用implements关键字吗?只对“第三方接口”使用,如Connection, Map之类的。这些东西的签名不会变的,所以没有方法名变更带来的问题,而dimple可以帮你避免使用"implements"关键字创建stub带来的当接口在新版本里面增加新的方法的时候造成的问题。
还有,jmock也有重构问题的
怀疑是Sun在编译器内部特制的。
对场景二,我要说的是:工具就老老实实当工具。你这里试图让我相信aspectj可以解决这个问题。但是通过交流下来,我没有发现aspectj在解决这个问题方面有什么好处,只是通过你逐渐地吞吞吐吐中发现aspectj先要对我的编程方式提出很多的要求。比如说必须按照aop的思想方法去思考和编码才成。我把这看成一个侵入,或者是aspectj的无能。
这倒不是我有意吞吞吐吐,因为我看过楼主做的一些东西,刚开始不知道楼主这样的高手对aop的代表产物aspectj了解的这么少。至于一种思维方式算不算一种侵入,这大概是仁者见仁智者见智的问题。比如楼主经常提到的各种模式也可以看成一种思维方式,那算不算侵入呢?可能楼主会说没有人强迫你非要用什么模式,aop也一样的,没有人强迫你非要用aop的方式编程,只是在很多情况下,采用一点aop可以达到事半功倍的效果。更何况,aop代表了java开发技术的一个方向,对它视而不见不是一个程序员应有的素质。
场景二从一开始就是一个模块内部的实现细节,通过强迫这个模块地实现遵循某个切面原则破坏了封装,增加了系统的复杂度。
对于场景二,我应该承认是我理解的错误。如果这个场景不具有共性,那么采用aop的方式的确是由大炮打蚊子的讽刺效果。呵呵。
场景二也不是制定某个路径就可以的,我需要的是对“某一个Connection实例”的decorate,不是不分青红皂白的拦截。比如:
这些代码可能都在一个类里面的(或者在两个类里面,具体在哪里我不关心)。这怎么“轻易”地用aspectj实现?
结合这个例子我觉得更能说明一个问题。就是aspectJ(AOP)对开发人员的思维习惯是有比较大的影响的。比如上例中我就会这样写:
我猜测楼主可能有些误解,pointcut针对的是方法或者属性操作(getter,setter),并不是针对类。所以,只要能明确代码中哪些方法是我们不信任的就可以产生出这个切面。其实,我想像不出什么样的场景下,在同一个方法中,对于来自同一个对象的两个实例,一个是信任的,一个是不信任的。
再强调一下,aspectJ既然是面向方面,那当然是针对具有共性的操作,也就是说能够产生这样一个横向的切面(无论这个切面是动态的还是静态的)。这是使用AOP技术的前提,如果这个前提都不存在,那自然就不应该使用AOP技术。回顾一下,楼主的帖子刚开始描述的场景,我起初认为还是具有很强的共性的。随着讨论的深入,楼主举的例子越来越不具有这种共性(很可能是我理解能力差,楼主最开始的例子也是想说一个不具有共性的情况),在这种情况下,即使用aspectJ的确能解决问题,也是完全没有必要的。
至于怎么用AOP的技术进行单元测试,在developerWorks上面有一篇比较详细的文章可以参考。
http://www.ibm.com/developerworks/cn/java/j-aspectj2/
场景一我就不说了。别人也都解释了。
对场景二,我要说的是:工具就老老实实当工具。你这里试图让我相信aspectj可以解决这个问题。但是通过交流下来,我没有发现aspectj在解决这个问题方面有什么好处,只是通过你逐渐地吞吞吐吐中发现aspectj先要对我的编程方式提出很多的要求。比如说必须按照aop的思想方法去思考和编码才成。我把这看成一个侵入,或者是aspectj的无能。
我们应该根据需要合理选择工具,而不是为了使用某个工具先强迫别人使用某种风格。场景二从一开始就是一个模块内部的实现细节,通过强迫这个模块地实现遵循某个切面原则破坏了封装,增加了系统的复杂度。老实说如果aspectj要这样使用,那对我这样在aop门外徘徊的人来说就太可怕了。
场景一不是拦截的问题,是我需要一个Connection对象的问题。我从哪里得到这个Connection对象?没有对象我怎么调用prepareStatement?没有我调用prepareStatement,aspectj拦截个啥?
我先说明一个问题,怎么在代码里判断一个Connection实例是不是需要进行处理的实例?无非两个方法,一个是根据调用Connection的代码路径来判断,另一种就是需要根据运行期的上下文环境来判断。但是大多数情况根据代码路径判断就足够了。如果楼主的意思是必须根据运行期的上下文来判断才足够的话,目前aspectJ的确还不能做到很直观的支持(因为aspectJ目前还不支持纯粹的runtime织入,这是aspectJ发展的一个方向)。
所以我才问你要一个示例客户代码或者测试代码,你说很简单,但是就不肯写出来。于是就姑且认为你不懂什么叫stub测试了。
楼主大概没看清楚我的回帖,我并不是说很简单,而是说因为aspectJ目前的织入方式是编译期织入或者classloading织入,所以并没有楼主所谓的客户代码。
场景二也不是制定某个路径就可以的,我需要的是对“某一个Connection实例”的decorate,不是不分青红皂白的拦截。比如:
这些代码可能都在一个类里面的(或者在两个类里面,具体在哪里我不关心)。这怎么“轻易”地用aspectj实现?
结合这个例子我觉得更能说明一个问题。就是aspectJ(AOP)对开发人员的思维习惯是有比较大的影响的。比如上例中我就会这样写:
我猜测楼主可能有些误解,pointcut针对的是方法或者属性操作(getter,setter),并不是针对类。所以,只要能明确代码中哪些方法是我们不信任的就可以产生出这个切面。其实,我想像不出什么样的场景下,在同一个方法中,对于来自同一个对象的两个实例,一个是信任的,一个是不信任的。
再强调一下,aspectJ既然是面向方面,那当然是针对具有共性的操作,也就是说能够产生这样一个横向的切面(无论这个切面是动态的还是静态的)。这是使用AOP技术的前提,如果这个前提都不存在,那自然就不应该使用AOP技术。回顾一下,楼主的帖子刚开始描述的场景,我起初认为还是具有很强的共性的。随着讨论的深入,楼主举的例子越来越不具有这种共性(很可能是我理解能力差,楼主最开始的例子也是想说一个不具有共性的情况),在这种情况下,即使用aspectJ的确能解决问题,也是完全没有必要的。
至于怎么用AOP的技术进行单元测试,在developerWorks上面有一篇比较详细的文章可以参考。
http://www.ibm.com/developerworks/cn/java/j-aspectj2/
场景一不是拦截的问题,是我需要一个Connection对象的问题。我从哪里得到这个Connection对象?没有对象我怎么调用prepareStatement?没有我调用prepareStatement,aspectj拦截个啥?
所以我才问你要一个示例客户代码或者测试代码,你说很简单,但是就不肯写出来。于是就姑且认为你不懂什么叫stub测试了。
场景二也不是制定某个路径就可以的,我需要的是对“某一个Connection实例”的decorate,不是不分青红皂白的拦截。比如:
这些代码可能都在一个类里面的(或者在两个类里面,具体在哪里我不关心)。这怎么“轻易”地用aspectj实现?
我给出的场景一的代码并不是拦截了项目中所有的prepareStatement调用。通过修改aspectj的inpath属性可以指定拦截任意路径下的代码(比如所有单元测试代码所在路径)。
ajoo有些偏见了,AOP和单元测试是一点也不会发生互斥的两件事,谈不上用aspectj的程序员写不写单元测试(就像很多用普通的java的程序员也不写单元测试一样)
场景二:用within也不管用啊。我又不是想全局性拦截untrustedFunction。untrustedFunction被在若干个地方调用。而其它地方的代码在它close connection的时候都工作的很好,我并不清楚贸然禁止它调用Connection.close()到底是福是祸。我知道的是我的模块想要自己控制connection,所以需要局部地架空Connection.close。
这个和上面的那个问题也可以用同样的方式解决。至于aspectj的适用范围的问题,我在上次的回帖中已经说了,aspectj已经将aop的复杂性屏蔽的很好了,需要补充的是,aop其实也挺适合用来做单元测试的工作的,至少是对它的一个有益的补充。具体的文章可以看看developerWorks上的相关文章。里面有几篇就是和单元测试相关的。
针对场景一:
对于场景二,我不太理解,为什么是指针对类的某些实例?这些实例用什么和其他实例加以区别?如果这个区别的方法比较简单的话,同样可以按照上面的做法处理。
当然,在项目中采用AspectJ会增加学习成本。具体用哪种方式更好,这要看开发人员对哪种技术比较熟悉。
没看懂这个aspect怎么实现的stub。写个客户程序的例子看看?前提是我测试的时候没有一个真正和数据库连接的Connection对象,只是想无中生有造出来一个假的作测试用。
场景二,比如说我拿到了一个Connection的实例,要把它传递给某一个不信任的函数someUntrustedFunction(myConnection)。我不希望这个函数关闭我的Connection实例。对此,我可以写:
这是一个decorator模式的应用。
针对场景一:
对于场景二,我不太理解,为什么是指针对类的某些实例?这些实例用什么和其他实例加以区别?如果这个区别的方法比较简单的话,同样可以按照上面的做法处理。
当然,在项目中采用AspectJ会增加学习成本。具体用哪种方式更好,这要看开发人员对哪种技术比较熟悉。
1。这个检查代价比较昂贵。基本上是现在要做的事情再多上一倍。
昂贵是指执行效率,占用空间,还是框架本身的代码行数?如果是框架作者的事情,那问题应该不大。做为一个feature排个序,有时间有必要就实现,没时间没必要就不实现。
那就区分对待好了。对于从Object继承下来的,明摆着就是一个同时实现一堆方法,但是又不想一次实现所有方法的语法糖。那就默认所有的方法都要改写要实现的接口的某个方法。对于从具体类集成下来的。那就不做检查。用@Override,让IDE给你检查好了。
是可以发现问题。只是加一点检查,可以fail fast。这和design by contract是一样的。没有那么多检查,代码该fail还是fail。只是多了一些检查,可以更快的失败,这样错误发生距离原因的距离就更近了一些。
4。对在production code里面的动态implement,象场景二提到的那种,同样的,unit test必须要写的。而一旦有了unit test,错误也就会迅速体现出来了。
难道dimple不光是服务于测试的?在production里咋用?
实现复杂就先估计一下。列在TODO里。有时间再做。
最理想的是IDE的插件啦。Eclipse可以检查@Override,而且@Override的Retention Policy是Source级别的。如果有插件,也可以做到同样的便利程度。
Ruby是Ruby嘛……到静态类型的语言中就该守静态类型的规矩,就应该利用静态类型带来的好处。在Ruby里还没法确定一个方法是不是存在于一个类中吧?只有调用了才知道(因为有method missing)。这就是Java的静态类型带来的束缚和好处。如果不利用,就浪费了。
增加了@Implement这个annotation。看看这个:
http://docs.codehaus.org/display/DIMPLE/Dimple+Tips
如果一个类的方法都需要检查,就在类上用@Implement好了。
个人喜欢state-based的单元测试。(定义见mock is not stub)。
可是有些时候,比如在测试一些使用java.sql.Connection, ibatis SqlMapClient等接口的类的时候,明显写stub很不好。(1,接口很大,有很多不相干的东西。2,版本一旦变化,这些接口可能跟着变化,如果写stub的话,就意味着stub要跟着这些第三方接口变化)
于是,只好mock。只好interaction based。只好每回内部实现一变就死盯着一坨坨的expectAndReturn找不再有效的expectation。
场景二:
一个遗留系统用自己的连接池,使用ConnectionManager.checkIn(Connection)来释放连接(而不是Connection.close)
为了重构,希望能够重载Connection.close(),让它调用checkIn()。
跟场景一的提到的原因一样,不希望直接写一个MyConnection implements Connection。于是只好自己写dynamic proxy,形成如下的代码:
private Connection conn; public Object invoke(Object proxy, Method method, Object[] args){ if((args==null || args.length==0) && method.getName().equals("close")){ ConnectionManager.checkIn((Connection)conn); } else { try { return method.invoke(conn, args); } catch(InvocationTargetException e){ throw e.getTargetException(); } } }
后面又发现这个变态的一流系统居然用自己的startMyTransaction, endMyTransaction(), rollbackMyTransaction()来搞事务处理!
于是又要继续判断method.getName().equals("startTransaction"), method.getName().equals("commitTransaction")等等等等。
dynamic proxy的代码难看的要死。
最近终于受不了了。愤然写了一个叫做酒窝的东西(dimple)。
所谓dimple,就是dynamic implementor的意思。我要用纯java语法来实现我想实现的方法(不是expectAndReturn,也不是if(method.getName().equals("method1"))),但是同时我又不愿意写“implements TheInterface”,以避免被迫处理那些我不关心的method。
用法如下:
场景一:
这样实现我的stub:
MyParameter myparam = ...; SqlMapClient client = (SqlMapClient) Implementor.proxy(SqlMapClient.class, new Object(){ public Object insert(String id, Object param) { assertEquals("test id", id); assertSame(myparam, param); return null; } }); assertNull(new SomeClassUsingSqlMapClient(client).runSomeInsert(myparam));
没有讨厌的expectAndReturn,不用担心parameter matcher。
场景二:
final Connection realConn = ...; Connection conn = (Connection) Implementor.proxy(Connection.class, new Object(){ public void close() { ConnectionManager.checkIn(realConn); } public void startTransaction(){ ConnectionManager.startMyTransaction(realConn); } public void commitTransaction(){ ConnectionManager.commitTransaction(realConn); } }, realConn);
于是,对close(), startTransaction, commitTransaction(),我们都直接调用ConnectionManager, 而对其它的method则委托给realConn。
这个Implementor类是纯java代码,除了dynamic proxy没有用任何其它技术。(没有enhancement,aop之类的)
个人感觉还是挺有用的。大家点评一下?
评论
71 楼
taowen
2007-01-05
看了一下发型包,已经0.4啦?现在有sample了么?有没有想好写一些什么经典应用场景呢?比如在做stub的情况下,和easymock做一个对比。至少可以告诉大家,在只要stub的情况下,犯不着搬出mock框架这么重的东西来。
70 楼
ajoo
2007-01-04
alin_ass 写道
用你这个取代mock类,万一接口方法名变了,重构挺麻烦的
我不是说:一般情况下你应该用implements关键字吗?只对“第三方接口”使用,如Connection, Map之类的。这些东西的签名不会变的,所以没有方法名变更带来的问题,而dimple可以帮你避免使用"implements"关键字创建stub带来的当接口在新版本里面增加新的方法的时候造成的问题。
还有,jmock也有重构问题的
69 楼
alin_ass
2007-01-04
用你这个取代mock类,万一接口方法名变了,重构挺麻烦的
68 楼
ajoo
2007-01-04
alin_ass 写道
我也不知道,那3个基本的编译期annotation怎么实现的?
e.g @override
e.g @override
怀疑是Sun在编译器内部特制的。
67 楼
alin_ass
2007-01-04
我也不知道,那3个基本的编译期annotation怎么实现的?
e.g @override
e.g @override
66 楼
ajoo
2007-01-04
以我对annotation的了解,还不知道怎么能够定制编译期检查。java5有这个功能么?
65 楼
alin_ass
2007-01-04
# Use @Implement annotation to automatically verify the method signature against the method being implemented.
看单元测试,这个@Implement只能做到运行期检查是不是接口里存在该方法,能做成编译期检查不是更好?
期待!
看单元测试,这个@Implement只能做到运行期检查是不是接口里存在该方法,能做成编译期检查不是更好?
期待!
64 楼
floating
2006-12-23
ajoo 写道
对场景二,我要说的是:工具就老老实实当工具。你这里试图让我相信aspectj可以解决这个问题。但是通过交流下来,我没有发现aspectj在解决这个问题方面有什么好处,只是通过你逐渐地吞吞吐吐中发现aspectj先要对我的编程方式提出很多的要求。比如说必须按照aop的思想方法去思考和编码才成。我把这看成一个侵入,或者是aspectj的无能。
这倒不是我有意吞吞吐吐,因为我看过楼主做的一些东西,刚开始不知道楼主这样的高手对aop的代表产物aspectj了解的这么少。至于一种思维方式算不算一种侵入,这大概是仁者见仁智者见智的问题。比如楼主经常提到的各种模式也可以看成一种思维方式,那算不算侵入呢?可能楼主会说没有人强迫你非要用什么模式,aop也一样的,没有人强迫你非要用aop的方式编程,只是在很多情况下,采用一点aop可以达到事半功倍的效果。更何况,aop代表了java开发技术的一个方向,对它视而不见不是一个程序员应有的素质。
ajoo 写道
场景二从一开始就是一个模块内部的实现细节,通过强迫这个模块地实现遵循某个切面原则破坏了封装,增加了系统的复杂度。
对于场景二,我应该承认是我理解的错误。如果这个场景不具有共性,那么采用aop的方式的确是由大炮打蚊子的讽刺效果。呵呵。
63 楼
ajoo
2006-12-22
floating 写道
ajoo 写道
场景二也不是制定某个路径就可以的,我需要的是对“某一个Connection实例”的decorate,不是不分青红皂白的拦截。比如:
realConn = ...; nonCloseable = decorate(realConn); nonCloseable.close();//被拦截 realConn.close();//不被拦截
这些代码可能都在一个类里面的(或者在两个类里面,具体在哪里我不关心)。这怎么“轻易”地用aspectj实现?
结合这个例子我觉得更能说明一个问题。就是aspectJ(AOP)对开发人员的思维习惯是有比较大的影响的。比如上例中我就会这样写:
public void untrustFunction() { realConn = ....; realConn.close(); } public void trustFunction() { realConn = ...; realConn.close(); }
我猜测楼主可能有些误解,pointcut针对的是方法或者属性操作(getter,setter),并不是针对类。所以,只要能明确代码中哪些方法是我们不信任的就可以产生出这个切面。其实,我想像不出什么样的场景下,在同一个方法中,对于来自同一个对象的两个实例,一个是信任的,一个是不信任的。
再强调一下,aspectJ既然是面向方面,那当然是针对具有共性的操作,也就是说能够产生这样一个横向的切面(无论这个切面是动态的还是静态的)。这是使用AOP技术的前提,如果这个前提都不存在,那自然就不应该使用AOP技术。回顾一下,楼主的帖子刚开始描述的场景,我起初认为还是具有很强的共性的。随着讨论的深入,楼主举的例子越来越不具有这种共性(很可能是我理解能力差,楼主最开始的例子也是想说一个不具有共性的情况),在这种情况下,即使用aspectJ的确能解决问题,也是完全没有必要的。
至于怎么用AOP的技术进行单元测试,在developerWorks上面有一篇比较详细的文章可以参考。
http://www.ibm.com/developerworks/cn/java/j-aspectj2/
场景一我就不说了。别人也都解释了。
对场景二,我要说的是:工具就老老实实当工具。你这里试图让我相信aspectj可以解决这个问题。但是通过交流下来,我没有发现aspectj在解决这个问题方面有什么好处,只是通过你逐渐地吞吞吐吐中发现aspectj先要对我的编程方式提出很多的要求。比如说必须按照aop的思想方法去思考和编码才成。我把这看成一个侵入,或者是aspectj的无能。
我们应该根据需要合理选择工具,而不是为了使用某个工具先强迫别人使用某种风格。场景二从一开始就是一个模块内部的实现细节,通过强迫这个模块地实现遵循某个切面原则破坏了封装,增加了系统的复杂度。老实说如果aspectj要这样使用,那对我这样在aop门外徘徊的人来说就太可怕了。
62 楼
poiuyt373
2006-12-22
现在有一个接口Connection,只需要close这个方法,其他的都不需要,dimple用dynamic proxy把一个oobject代理成Connection,这样就可以使用close了。
aop可以拦截方法调用,如果是一个现成的Connection,那没有问题,问题是,现在没有一个Connection,aop拦截什么?mixin的话,也是需要cglib增强的吧
aop可以拦截方法调用,如果是一个现成的Connection,那没有问题,问题是,现在没有一个Connection,aop拦截什么?mixin的话,也是需要cglib增强的吧
61 楼
floating
2006-12-22
ajoo 写道
场景一不是拦截的问题,是我需要一个Connection对象的问题。我从哪里得到这个Connection对象?没有对象我怎么调用prepareStatement?没有我调用prepareStatement,aspectj拦截个啥?
我先说明一个问题,怎么在代码里判断一个Connection实例是不是需要进行处理的实例?无非两个方法,一个是根据调用Connection的代码路径来判断,另一种就是需要根据运行期的上下文环境来判断。但是大多数情况根据代码路径判断就足够了。如果楼主的意思是必须根据运行期的上下文来判断才足够的话,目前aspectJ的确还不能做到很直观的支持(因为aspectJ目前还不支持纯粹的runtime织入,这是aspectJ发展的一个方向)。
ajoo 写道
所以我才问你要一个示例客户代码或者测试代码,你说很简单,但是就不肯写出来。于是就姑且认为你不懂什么叫stub测试了。
楼主大概没看清楚我的回帖,我并不是说很简单,而是说因为aspectJ目前的织入方式是编译期织入或者classloading织入,所以并没有楼主所谓的客户代码。
ajoo 写道
场景二也不是制定某个路径就可以的,我需要的是对“某一个Connection实例”的decorate,不是不分青红皂白的拦截。比如:
realConn = ...; nonCloseable = decorate(realConn); nonCloseable.close();//被拦截 realConn.close();//不被拦截
这些代码可能都在一个类里面的(或者在两个类里面,具体在哪里我不关心)。这怎么“轻易”地用aspectj实现?
结合这个例子我觉得更能说明一个问题。就是aspectJ(AOP)对开发人员的思维习惯是有比较大的影响的。比如上例中我就会这样写:
public void untrustFunction() { realConn = ....; realConn.close(); } public void trustFunction() { realConn = ...; realConn.close(); }
我猜测楼主可能有些误解,pointcut针对的是方法或者属性操作(getter,setter),并不是针对类。所以,只要能明确代码中哪些方法是我们不信任的就可以产生出这个切面。其实,我想像不出什么样的场景下,在同一个方法中,对于来自同一个对象的两个实例,一个是信任的,一个是不信任的。
再强调一下,aspectJ既然是面向方面,那当然是针对具有共性的操作,也就是说能够产生这样一个横向的切面(无论这个切面是动态的还是静态的)。这是使用AOP技术的前提,如果这个前提都不存在,那自然就不应该使用AOP技术。回顾一下,楼主的帖子刚开始描述的场景,我起初认为还是具有很强的共性的。随着讨论的深入,楼主举的例子越来越不具有这种共性(很可能是我理解能力差,楼主最开始的例子也是想说一个不具有共性的情况),在这种情况下,即使用aspectJ的确能解决问题,也是完全没有必要的。
至于怎么用AOP的技术进行单元测试,在developerWorks上面有一篇比较详细的文章可以参考。
http://www.ibm.com/developerworks/cn/java/j-aspectj2/
60 楼
ajoo
2006-12-22
floating 写道
我给出的场景一的代码并不是拦截了项目中所有的prepareStatement调用。通过修改aspectj的inpath属性可以指定拦截任意路径下的代码(比如所有单元测试代码所在路径)。
场景二:用within也不管用啊。我又不是想全局性拦截untrustedFunction。untrustedFunction被在若干个地方调用。而其它地方的代码在它close connection的时候都工作的很好,我并不清楚贸然禁止它调用Connection.close()到底是福是祸。我知道的是我的模块想要自己控制connection,所以需要局部地架空Connection.close。
这个和上面的那个问题也可以用同样的方式解决。至于aspectj的适用范围的问题,我在上次的回帖中已经说了,aspectj已经将aop的复杂性屏蔽的很好了,需要补充的是,aop其实也挺适合用来做单元测试的工作的,至少是对它的一个有益的补充。具体的文章可以看看developerWorks上的相关文章。里面有几篇就是和单元测试相关的。
ajoo 写道
场景二:用within也不管用啊。我又不是想全局性拦截untrustedFunction。untrustedFunction被在若干个地方调用。而其它地方的代码在它close connection的时候都工作的很好,我并不清楚贸然禁止它调用Connection.close()到底是福是祸。我知道的是我的模块想要自己控制connection,所以需要局部地架空Connection.close。
这个和上面的那个问题也可以用同样的方式解决。至于aspectj的适用范围的问题,我在上次的回帖中已经说了,aspectj已经将aop的复杂性屏蔽的很好了,需要补充的是,aop其实也挺适合用来做单元测试的工作的,至少是对它的一个有益的补充。具体的文章可以看看developerWorks上的相关文章。里面有几篇就是和单元测试相关的。
场景一不是拦截的问题,是我需要一个Connection对象的问题。我从哪里得到这个Connection对象?没有对象我怎么调用prepareStatement?没有我调用prepareStatement,aspectj拦截个啥?
所以我才问你要一个示例客户代码或者测试代码,你说很简单,但是就不肯写出来。于是就姑且认为你不懂什么叫stub测试了。
场景二也不是制定某个路径就可以的,我需要的是对“某一个Connection实例”的decorate,不是不分青红皂白的拦截。比如:
realConn = ...; nonCloseable = decorate(realConn); nonCloseable.close();//被拦截 realConn.close();//不被拦截
这些代码可能都在一个类里面的(或者在两个类里面,具体在哪里我不关心)。这怎么“轻易”地用aspectj实现?
59 楼
floating
2006-12-22
ajoo 写道
还是不对。aspectj的这个锤子和我的要求还是有差别的。
场景一:我是做个stub,不是要拦截所有的prepareStatement呀。easymock用过吧?一样的道理呀。莫非说得还不够明白?还是用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上的相关文章。里面有几篇就是和单元测试相关的。
58 楼
ajoo
2006-12-21
还是不对。aspectj的这个锤子和我的要求还是有差别的。
场景一:我是做个stub,不是要拦截所有的prepareStatement呀。easymock用过吧?一样的道理呀。莫非说得还不够明白?还是用aspectj的程序员从来不写单元测试的?
场景二:用within也不管用啊。我又不是想全局性拦截untrustedFunction。untrustedFunction被在若干个地方调用。而其它地方的代码在它close connection的时候都工作的很好,我并不清楚贸然禁止它调用Connection.close()到底是福是祸。我知道的是我的模块想要自己控制connection,所以需要局部地架空Connection.close。
拿起aspectj的锤子之前能不能先看看这个东西是不是钉子再说?
场景一:我是做个stub,不是要拦截所有的prepareStatement呀。easymock用过吧?一样的道理呀。莫非说得还不够明白?还是用aspectj的程序员从来不写单元测试的?
场景二:用within也不管用啊。我又不是想全局性拦截untrustedFunction。untrustedFunction被在若干个地方调用。而其它地方的代码在它close connection的时候都工作的很好,我并不清楚贸然禁止它调用Connection.close()到底是福是祸。我知道的是我的模块想要自己控制connection,所以需要局部地架空Connection.close。
拿起aspectj的锤子之前能不能先看看这个东西是不是钉子再说?
57 楼
floating
2006-12-21
场景一的情况:
客户程序就是很普通的调用啊。没有什么特别的地方。aspectj会织入到所有对prepareStatement调用的代码中。如果是源代码级别的,可以选择编译时织入。如果只有二进制代码,没有源码,那需要在classloading时候织入,这时候稍微复杂一些,需要告诉jvm使用aspectj的classloader。
场景二的情况:
只需要在例子上面的pointcut中对untrustFunction所在的类使用within关键字就可以了。代码片段如下:
说明一下,虽然用aspectJ可以方便地解决LZ提出的问题,但是我也挺喜欢LZ这类基于Dynamic Proxy方式的实现的。就像aspectJ将AOP的复杂性包装起来了一样,LZ的dimple也已经把使用Proxy的复杂性屏蔽的很好了。正如ajoo举的几个例子那样,dimple使用起来很简单,没有什么学习成本。基于上面的原因,我觉得我给出的方法更接近于启发一种解决问题的思路(比如,在没有找到dimple这个工具的情况下,呵呵)。
当然AOP技术发展的也很快,如果大家有精力,我还是建议大家关注一下aspectJ的。毕竟,AOP对开发程序的思维模式的影响是比较大的。
客户程序就是很普通的调用啊。没有什么特别的地方。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对开发程序的思维模式的影响是比较大的。
56 楼
ajoo
2006-12-21
再举一个adapter的例子。
不知道大家遇到过这样的情况没有。有一个api,有这个接口签名:
它仅仅使用get()一个方法,但是因为种种原因,并没有使用最小接口(我认为这种设计是非常正常的),而是用了这个java.util.Map接口。
在使用这个api的时候,我们不幸的没有一个Map对象,而只有一个proprietary的PropertyMap(或者HttpServletRequest也行)对象:
需要写个adapter吧?目标代码如下:
那么这个convertToMap怎么写呢?
Map接口相当的肥厚阿。继承AbstractMap的话要实现entrySet(),我们也没有。而我们明明知道runApi()只需要一个能支持get()的Map就成了。
一个方法呢,就是用dimple,下面是一个pojo的adapter(不依赖dimple):
只需要实现我们关心的方法(一个安全的方法,可以显然PropertyMapAdapter implements Map,这样就可以用eclipse的代码生成来选择性地实现这些我们关心的方法,不会有typo。),不用管其它的乱七八糟的。
然后使用dimple,实现convertToMap如下:
搞定。
可见,dimple并不是仅仅可以用做aop的interceptor,它还是一个克服静态类型局限的OO利器亚。而且不象aspectj,相当的light-weight,没有什么侵入性。
不知道大家遇到过这样的情况没有。有一个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,没有什么侵入性。
55 楼
ajoo
2006-12-20
floating 写道
ajoo 写道
场景一:
做stub。只实现一百个方法中的一个或者两个
场景二:
动态地仅仅intercept一个对象,而不是全局地针对一个类。
aspectj怎么做?既然就几行代码,写一下不是很容易?
做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模式的应用。
54 楼
floating
2006-12-20
ajoo 写道
场景一:
做stub。只实现一百个方法中的一个或者两个
场景二:
动态地仅仅intercept一个对象,而不是全局地针对一个类。
aspectj怎么做?既然就几行代码,写一下不是很容易?
做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会增加学习成本。具体用哪种方式更好,这要看开发人员对哪种技术比较熟悉。
53 楼
ajoo
2006-12-19
也增加了implementedBy和willImplement等函数来帮助在java 5以前的代码做fail fast。(为什么把类型检查设计成可选?因为不是所有时候这个检查都是需要的,有时候,一个method没有被用来实现接口里面的某个method是可以允许的)
52 楼
ajoo
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好了。
相关推荐
设计了一种酒窝状换热板,利用Fluent软件对酒窝板、平板及波纹板的换热及压降特性进行了模拟,对这3种板在不同流速下板间流道内壁面平均换热系数、换热量、平均努塞尔数以及摩擦阻力的变化特性进行了对比分析。...
《小酒窝》教学设计是针对音乐教育领域的一份详细教案,旨在教授小学生学习和表演歌曲《小酒窝》,同时欣赏并理解乐曲《我们多么幸福》。教学目标旨在通过多样的教学方法,培养学生的音乐感知力、创造力以及对音乐...
人音三年级上册小酒窝PPT教案学习.pptx
这篇PPT是一个针对小学三年级学生的音乐学唱课程,主要教授歌曲《小酒窝》。《小酒窝》是一首充满快乐和甜美的歌曲,适合孩子们学习,以培养他们对音乐的兴趣和基本的音乐素养。 首先,课程介绍了歌曲的速度和情绪...
人音版三年级上册小酒窝PPT教案学习.pptx
这篇文档是关于三年级上册音乐课程的一个教案,教学内容为《小酒窝》这首歌,采用的是人音版教材,以五线谱的形式呈现。教学目标是让学生能够学唱这首歌曲,体验并表达出歌曲中甜美、活泼的情绪,同时激发他们对童年...
【标题】"酒窝窝DIGG类web2.0全站 -ASP源码.zip" 提供的是一款基于ASP编程语言的Web2.0网站源码,适用于构建互动性强、用户参与度高的社交平台。Web2.0是互联网发展的一个重要阶段,强调用户的交互性、共创性和分享...
【酒窝143】项目概述 "酒窝143"这个名称可能是指一个项目的代号或主题,但它本身并没有直接提供关于IT技术的知识点。不过,从提供的压缩包文件名“dimple143-master”来看,我们可以推测这可能是一个开源软件项目...
15. 酒窝性状遗传分析:儿子、儿媳均有酒窝但生出无酒窝孩子,说明他们都是杂合子(Aa),再生一个孩子有酒窝的概率为75%,无酒窝的概率为25%。 以上知识点涵盖了生物学的多个核心概念,如生物生殖、发育、遗传规律...
rcdimple | htmlwidget rcdimple是在凹坑片的版本 。 我们的目标是直接从R漂亮,可定制的d3.js图表,并且只需最少的代码并且不了解JavaScript。 首先,可以使用devtools::install_github进行安装。...
- 若有酒窝为显性,无酒窝为隐性,有酒窝的祖父母、儿子、儿媳可能基因型为Aa,孙子无酒窝则基因型为aa。 - 儿子、儿媳再次生育,有酒窝孩子的概率为50%,因为他们都是Aa,子代可能为AA或Aa,其中Aa表现为有酒窝。...
- 扩句与缩句:扩句是在简单句的基础上增加修饰词,使其更具体,如"脸蛋印着酒窝"变为"可爱的脸蛋印着迷人的酒窝";缩句则是去除修饰成分,保留句子核心,如"脸蛋印着酒窝"可以简化为"脸蛋印着酒窝"。 3. **反问...
- 小明的家庭中,如果有酒窝是显性(A),无酒窝是隐性(a),小明父亲无酒窝,因此其基因型为aa,母亲有酒窝,基因型为AA或Aa。反之,如果无酒窝是显性,有酒窝是隐性,则母亲的基因型为aa,父亲必须是Aa,因为...
使用python给你beep个"祝你生日快乐" 和 "小酒窝" 简介不够代码来凑 import winsound def winbeep_di(): winsound.Beep(1000, 100) ....此处省略500宇宙代码 if __name__ == '__main__': winbeep_di() ...
描述时,可以用比喻、拟人等修辞手法增加语言的表现力,如“笑起来非常好看,像是小酒窝里面装了酒一样”。外貌描述不仅要细致,还要力求生动、有趣,让人印象深刻。 其次,性格特征的呈现是自画像作文的重点。性格...
此外,一对无酒窝的夫妇无法生育出有酒窝的子女,因为有酒窝的基因D是显性的,而决定无酒窝的基因d是隐性的,所以无酒窝的夫妇基因型只能是dd,无法提供D基因给子女。 在能力提升环节,卷舌与非卷舌的例子进一步...
例如,"脸蛋印着酒窝"可以扩写为"可爱的脸蛋印着迷人的酒窝",缩写为"脸蛋印着酒窝"。 - **反问句与陈述句的互换**:反问句常用来强调陈述,转换时要去掉反问词,调整语气,如"我们是小学生,难道不要好好学习吗?...
3. 你笑起来的样子最为动人,两片薄薄的嘴唇在笑,长长的眼睛在笑,腮上两个陷得很举动的酒窝也在笑。 4. 青翠的柳丝,怎能比及你的秀发;碧绿涟漪,怎能比及你的眸子; 5. 你热情似火,你的微笑让我神魂颠倒 这些...
例如:“我是一个阳光的女孩,虽然谈不上特别美丽,但也不丑陋,上翘的嘴角和薄薄的嘴唇,笑起来还有浅浅的酒窝。” 3. **性格特点**:性格特征是自我介绍中的重要组成部分,能反映你的内在世界。如:“我性格热情...
例如:“每当想起我们在一起同窗时的纯真岁月,你从十岁多的小丫头始终到二十多年后的今日,总是漂美丽亮的,留着迷人的酒窝,未见过你扎辫子……,其情其景,恍如昨日!”这种描述不仅表达了写信人对美好时光的怀念...