to ajoo

原来ajoo这么用心,提出如此多问题,我也不一定能回答,权当交流讨论:

关于AspectJ,noxel也说了,我们都同意它繁重且破坏类的封装性,我个人也倾向于动态AOP,将推出一篇相关文章。

关于你用Bridge模式实现的代码,其实类似权限控制实现的代理模式,你可能要为行为类都要实现一个相应的包装,这很琐碎,这是横向思维的结果。

如果使用AOP实现,只需要一个Aspect就可以了,可见其简洁和高度抽象性。

如果把Bridge模式中多个行为看成是一条条横向线条,那么,AOP是从纵向将这些线条归类,变成一个线条。

banq,
我不是很清楚“行为类都要实现一个相应的包装”的意思。
实际上,我写的那个示例,只需要做一个LockedWorker类,它完全对应于Lock aspect呀。
其它的实现createData()和updateData()的类不需要做什么包装啊。和那个aspectJ的方案比起来差不多吧?

还是你指的是这些类都必须实现一个固定的接口?不过我比较推崇接口编程,给一个规范定义一个接口我觉得挺好的啊。

还有,我那个bridge实现是利用了语言的静态类型系统,所以需要一个接口。其实,就象很多connection pool的实现一样,改为采用dynamic proxy也是一样的,只不过是失去一点类型安全性,换来一些灵活性而已。(这其实等于用语言built-in的reflection来代替了那个明确定义的接口)。这个LockedWorker如果改为采用dynamic proxy实现,也是一样的。我个人认为完全是一个思想的两种不同实现。

只不过似乎你把它认为是aop的思想,而我仍然把它看成一个bridge pattern。
而这正是我感到迷惑的地方:dynamic proxy, interceptor到底为什么叫aop?它们和bridge有什么本质的区别?如果没有,那么何必抛开已经很成熟的bridge模式,而搞一个复杂的aop?
还是说,bridge的内涵有点含混,而aop是把这种concern oriented的思想的一个更清晰明确的描述,虽然可以用bridge来实现aop的意图?
换句话说,aop和concern并不矛盾,它们只是对一个事务的侧重点不同的描述?

我总是觉得,aop应该有一些应用,可以明显看出bridge模式的无能为力和aop的优势,仅仅说bridge有点繁琐好像不是很充足的理由来justify aop的引入。
所以,在我前面给出的那个link里,我自作聪明地给出了一个我自己想象的aop的本质(看来也许是错的)

还有,我上面提出的几个具体问题主要是我对aspectJ这种解决方案的语义定义上的怀疑。实际上我看不到一个清楚定义的aspectJ的织入语义。而bridge的实现就没有这些不清楚的语义,一切都一目了然。

写一个把上面的bridge实现改为dynamic proxy的伪码:

class LockAspect{
Lock l=new ...;
invoke(...){
if method_name in ("createData", "updateData") and parameter list is empty{
acquire lock
call the method
release lock
}
else{call the method}
}
}
其实本质不是一样的么?难道aop就是指用dynamic proxy实现bridge/decorator等模式?

看了noxel给的连接。
我想,如果测试的例子能够稍微加入一点递归就好了。
如果helloworld()调用了helloworld(), 那么,结果是打印每一个helloworld()的调用还是只打印最顶层的,就很有区别。

如果只打印最顶层的helloworld,那么,aop的效果就和用bridge或者dynamic proxy自制的没有什么区别了。都是在不改变原来代码的基础上提供一些额外的功能。充其量,aop的实现不过是把这种类似的bridge功能通过库给予支持,然后让程序员编辑xml文件而不是写自己的bridge。个人对这种东西的意义表示怀疑。

但是,如果这个tracing可以打印每一次调用,那么,意义就完全不同了。就象我自己胡思乱想的那样,这种打破了黑盒,被编织的东西的内部实现会影响编织模块的行为的模型,是bridge无法模拟的。
比如,用bridge的话,你可以这样:
class Traced implements HelloWorld{
void hello(){
System.out.println("hello world");
hw.hello();
}
HelloWorld hw;
}
但是,因为hw内部的实现是对bridge透明的,它内部是否使用了递归调用还是什么其它的技术,你无权知道,所以,你不可能达到不改动原始代码,而对每次递归调用都打印trace的结果。

这就是一种bridge所无法取代的aop了。

当然,用这种东西,象banq给的lock的例子也只怕最好不用aop了。因为不能保证实现内部不会互相调用或者递归调用。

其实你使用了动态代理的“伪码”,已经表示你的考虑角度发生变化,以切面考虑了,因为动态代理的方法调用明显是一种不同于正常调用的角度。

还有Mixin,在传统Java的单继承结构中,多个extends是无能为力的,所以一些模式无法很好地实现,AOP提供了Mixin实现。

我也赞同:关于AOP能实现的,模式也能实现。只是在灵活性程度有所区别而已,而且我感觉AOP可以融入架构中,帮助固定架构,模式则不一定了,只能在被固定后的架构中使用。

不知道啊。也许我一直没有意识到我的思想方法已经是aop了吧?:)
反正我是觉得这个aop来给某一类模块提供额外的服务的方法和我认为的OO的设计思想没什么区别。所以我才奇怪它和bridge的区别在何处。从我的理解,dynamic proxy和前面的bridge只是实现方法的差异而已。


对了,banq, mixin是怎么回事?能否介绍一下?

dynamic proxy 只是aop的实现手段,

和模式进行比较的是不是用“ 面向方面” 更确切一些 :)

mixin是指类 A 本身没有继承 类B,但是经过aop之后,你可以把 A 造型成 B,使用B得方法。

我设想的理想的pointcut,advice模型是这样的

各个advice不用继承各个框架的抽象advice类(可以使用自身的introduction机制来达到继承的目的)

这样写好的advice可以在各个aop框架中自由移植,不然象现在这样,我每换一种框架都得换一套api集合。

另:这个想法来自IOC 在aop框架设计中一样可以用到,不要强制你的业务感知来自框架得东东,而让你的框架去感知你的业务。

大家如果有兴趣可以讨论一下实现。

>让你的框架去感知你的业务
定义配置文件格式,使用XML文件啦。

to ajoo
可以看看JBoss 3 架构师rickard.oberg关于AOP的随想,他认为最大优点是重用性。

这位20多岁的瑞典小伙子rickard.oberg是JBoss 3架构师、XDoclet和WebWork开源创始人,现在和JBoss决裂,可谓是Java开源精神领袖,他批评.NET为愚昧、认为JDO无法抗衡CMP等言论都是有一定可参照性。

http://www.jroller.com/page/rickard/20020928

AOP带来的一个冲击是,类似OFBiz这样的软件将会重写,或重新架构,否则必被淘汰,很多公司的产品和项目是基于OFBiz的,看来得有思想准备了。

Java领域变化太快,稍微不小心自己落入一个陷阱,所以,公司或项目的架构选型更应该着重标准,而不是具体开源产品,例如EJB标准,AOP为EJB容器实现提供非常好的架构,如果你的应用系统遵循EJB,那么AOP对你的冲击很小。

因为AOP将成为EJB容器实现的核心了,所以有人认为不需要EJB了,其实这会给我们应用系统和项目带入一个另外陷阱,我们是最终用户,在迅速变换的技术发展前景下,我们只能选择标准,标准帮助我们回避了技术发展带来的风险。

这正如有些人认为跟从微软就可以,因为微软是标准,它会把最新技术和概念如AOP融入它的标准实现中。在Java领域,我推荐认定J2EE标准,AOP的出现已经证明J2EE标准当初划分容器和应用系统两个层面思想的可预见性。

新标准会兼容旧标准,这是标准最为本质的核心,这个核心保护了我们最终用户的投资。

Java领域争奇斗艳,罂粟和鲜花同时存在,如何加强你的辨别力?
答案:经常到J道之类专业网站看看。呵呵,不是广告,是真话。TSS太乱,容易迷失方向,J道宗旨是永远站在最终用户这一边,因为板桥我一直奋斗在项目设计编码第一线啊。这也算春节新年祝词吧。

> >让你的框架去感知你的业务
> 定义配置文件格式,使用XML文件啦。

这就是spring和picocontainer 两个IOC框架之争拉

spring 是把每个组件做成java bean ,用xml把定义他们的交互关系,spring负责维护这些关系。
pico是用constructor 来定义他们之间的关系,pico维护关系

pico简单精巧,spring可提供复杂的功能,但是揉入了太多东西。


如果有一个aop框架即能提供aspectwekz的灵活,而各个advice又不用继承框架的类,使用pojo或者java bean 来定义advice 和mixin,可以自由的通过xml文件,或是constructor 来维护两者的关系,那就perfect了。而且advice可以自由的在其他地方使用,那样才叫真正的提高复用性。

只是如何用一种优雅的方式在advice中对pointcut进行访问,我还没有想好。

to ajoo
这个网址分别使用了Java和AspectJ实现GoF设计模式,从对比中,你也许可发现AOP独特的优势,我没仔细看,如果你看了有什么想法,一起讨论?

http://www.cs.ubc.ca/~jan/AODPs/

to noxel:
我刚拉了Spring框架,在研究中,看了架构设计,感觉复杂罗嗦了点,提到EJB也是若即若离,没有明显特征....

noxel:
我怎么觉得dynamic proxy是proxy的另一种实现啊?
本来是把aop和传统OO的patterns相比的。不过,比来比去,发现aop似乎唯一的区别就是使用dynamic proxy。:(

好吧,让我们不要争论dynamic proxy和传统的OO方法论是否统一,就把它搁置一下,然后看看aop除了dynamic proxy还有什么其它的优点。


或者说,如果我们用OO+dynamic proxy,是否可以达到aop能达到的功能。

这个mixin我还是有点糊涂,难道它可以让你extends类吗?那样不是可能会造成多继承?从banq给的例子,似乎mixin倒是可以让类implements另外的interface。这我没有疑问。

banq:
粗略地看了你给的这个link。多谢了。
不过,也许我不是看得很懂,作者似乎是用OO实现了一个observer pattern,然后说,看,这个实现有一些地方不能重用。
我同意这一点。
可是,然后作者就开始比较aop的实现和这个实现。
这里面的推论似乎不够严密呀。
OO的那个示例确实没有考虑到某些东西的重用,但是这不代表用OO就不能实现那个目标吧?让一只猫吃了一条小鱼,就能表示陆地动物比海洋动物厉害?

下面,我把那个例子中的aop的实现贴一下,然后试着用OO的方法做一个对应的实现大家看看。


public abstract aspect ObserverProtocol {
protected interface Subject { }
protected interface Observer { }
abstract protected pointcut subjectChange(Subject s);
abstract protected void updateObserver(Subject s, Observer o);
private WeakHashMap perSubjectObservers;
protected List getObservers(Subject s) {
if (perSubjectObservers == null) {
perSubjectObservers = new WeakHashMap();
}
List observers = (List)perSubjectObservers.get(s);
if ( observers == null ) {
observers = new LinkedList();
perSubjectObservers.put(s, observers);
}
return observers;
}
public void addObserver(Subject s,Observer o){
getObservers(s).add(o);
}
public void removeObserver(Subject s,Observer o){ getObservers(s).remove(o); }
after(Subject s): subjectChange(s) {
Iterator iter = getObservers(s).iterator();
while ( iter.hasNext() ) { updateObserver(s, ((Observer)iter.next())); }
}
}

这个aspect提供了需要重用的对observer-subject映射的管理,以及当subject变化时的notify代码。

然后,一个子aspect来对Line和Point做observer pattern
aspect CoordinateObserver extends ObserverProtocol{
declare parents: Point implements Subject;
declare parents: Line implements Subject;
declare parents: Screen implements Observer;
protected pointcut subjectChange(Subject s):
(call(void Point.setX(int))|| call(void Point.setY(int))
|| call(void Line.setP1(Point))|| call(void Line.setP2(Point)))
&& target(s);


protected void updateObserver(Subject s, Observer o){
((Screen)o).display("Coordinate change.");
}
}
这个子aspect把Subject接口mixin到Point, Line等类;然后定义这两个类中的一些操作为变化切面;最后,又对Screen的observer在坐标变化时的逻辑做了实现。

先提一下几个问题,(当然,可能是我对aspectJ不熟悉造成的,如果错了,希望指正)

1。象我前面所说,如果碰巧几个被定义为变化的方法互相调用了,或者递归调用了(这些都是Point等的内部实现逻辑,aspect不应该关心),那么这个添加的advice怎么处理?如果对每个调用都处理,逻辑就乱了!

2。代码有重用的考虑,也应该有pluggability的考虑。如果我在动态的时候需要选择不同的aspect来得到不同的observer实现,如何处理?

3。这个updateObserver()实现在observer不只是Screen的时候,是否还要通过instanceof来判断Observer啊?如果某个observer观察了不同的subject,那么是否也需要用instanceof来判断subject啊?一堆嵌套的if-else,很坏的味道吧?


这个aspect实现我比较喜欢的地方是那个subjectChange的实现,可以很方便地制定某几个函数调用为一个切面。


下面,来用OO的方法写一个对应的实现:

兵马未动,接口先行,首先定义接口:
interface Subject{}

interface Observer{
void notify(Subject sub);
}

管理观察者和被观察者的也是一个接口:
interface ObserverRegistrar{
public void addObserver(Subject s,Observer o);
public void removeObserver(Subject s,Observer o);
}


然后是对应aspect ObserverProtocol的可重用实现:
class ObserverProtocol implements Observer, ObserverRegistrar{
private WeakHashMap perSubjectObservers;
protected List getObservers(Subject s) {
//使用上面那个aspect里面完全相同的代码
}
public void addObserver(Subject s,Observer o){
//使用上面那个aspect里面完全相同的代码
}
public void removeObserver(Subject s,Observer o){
//使用上面那个aspect里面完全相同的代码
}
public void notify(Subject sub){
Iterator iter = getObservers(s).iterator();
while ( iter.hasNext() ) { ((Observer)iter.next()).changed(sub); }
}
}

这个类和前面的aspect的唯一区别是它不定义after subjectChange这个advice。它实际上是作为一个Observer存在的。当subject变化时,需要subject来通知它。

当然,前面那个子aspect的用意就是可以让这个通知自动化。而不需要Point, Line这些类关心observer pattern的存在。

于是,下面是如何实现对应那个子aspect的代码:
首先,手工mixin
class PointSubject implements Point, Subject{
//all delegate methods
Observer ob;
Point pt;
public void setX(int x){pt.setX(x);ob.notify(this);}
//其它的委托函数
}
class LineSubject implements Line, Subject{
Observer ob;
Line ln;
public void setP1(Point p){
ln.setP1(p);
ob.notify(this);
}
//其它的委托函数
}

class Screen implements Observer{
...
public void notify(Subject sub){
if(sub instanceof Line){...}
else if(sub instanceof Point){...}
}
}
这两个decorator让Point和Line和observer相关联,当然,原始的Line和Point实现并不需要关心observer。
Screen的实现确实是关心subject的变化的,是subject-aware的。我认为这比aspect例子里面的方法要好。而且,毕竟这里没有什么要重用的代码。


此处,如果比较一下这个手工mixin和前面子aspect里面的mixin和切面定义,就会发现,它比aspect方法的切面繁琐,需要手工写所有Point, Line接口里面的委托,也需要手工对每个作为subjectChange切面的方法调用notify。
这时候,可以使用dynamic proxy来得到更好的效果。当然,为了得到类似于aspect里面的指定切面的方便,可能还需要稍费一点心思。(可以做一个比较方便使用的库来用OO的语法做方法选择)
具体方法略。


下面,把这个decorator用facade对用户公开:
class ObserverFacade implements Observation{
ObserverProtocol reg= new ObserverProtocol();

public PointSubject registerPoint(Point p){
return new PointSubject(p, reg);
}
public LineSubject registerLine(Line l){...}
public void addObserver(Subject s, Observer o){
reg.addObserver(s, o);
}
public void removeObserver(Subject s, Observer o){reg.removeObserver(s, o);}

}

最后,示例客户代码如下:
Point pnt = ...;
Line ln = ...;
Screen scr = ...;
//下面处理observer
ObserverFacade facade = new ObserverFacade();
ObservedPoint opnt = facade.register(opnt);
ObservedLine opln = facade.register(opln);
facade.addObserver(opnt, scr);
facade.addObserver(opln, scr);


结论:用OO+dynamic proxy,我们也可以实现示例中aspect所能做到的事。而且,没有上面给aspect提出的三个问题。

唉,原来有个code标签啊。土了!
重新贴一遍代码吧


interface Subject{}

interface Observer{
void notify(Subject sub);
}
interface ObserverRegistrar{
public void addObserver(Subject s,Observer o);
public void removeObserver(Subject s,Observer o);
}

class ObserverProtocol implements Observer, ObserverRegistrar{
private WeakHashMap perSubjectObservers;
protected List getObservers(Subject s) {
if (perSubjectObservers == null) {
perSubjectObservers = new WeakHashMap();
}
List observers = (List)perSubjectObservers.get(s);
if ( observers == null ) {
observers = new LinkedList();
perSubjectObservers.put(s, observers);
}
return observers;
}
public void addObserver(Subject s,Observer o){
getObservers(s).add(o);
}
public void removeObserver(Subject s,Observer o){ getObservers(s).remove(o); }
public void notify(Subject sub){
Iterator iter = getObservers(s).iterator();
while ( iter.hasNext() ) { ((Observer)iter.next()).changed(sub); }
}
}

class PointSubject implements Point, Subject{
//all ob methods
Observer ob;
Point pt;
public void setX(int x){pt.setX(x);ob.notify(this);}
}
class LineSubject implements Line, Subject{
Observer ob;
Line ln;
public void setP1(Point p){
ln.setP1(p);
ob.notify(this);
}
}

class ObserverFacade implements ObserverRegistrar{
ObserverProtocol ob = new ObserverProtocol();

public PointSubject registerPoint(Point p){
return new PointSubject(p, ob);
}
public LineSubject registerLine(Line l){...}
public void addObserver(Subject s, Observer o){ob.addObserver(s, o);}
public void removeObserver(Subject s, Observer o){ob.removeObserver(s, o);}

}