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提出的三个问题。