asato <asato@ncfreak.com>
最終更新日 : 2004/6/5 (2002/9/26 より)
概要
まず最初に、オブジェクト指向 Oserver パターンの実装における欠点について述べます。その後、様々な動機に基づいて、AspectJ による、いくつかの Observer パターンの実装を紹介します。
Observer
Observer パターンのバリエーション
しかしながら、ここでは、主に GoF で紹介されている Observer パターンを AspectJ を使って実装することに焦点を当てています。そのため、何を Observer パターンと見なすのかどうか、や、どうなっていれば Observer パターンなのか、あるいは、どこまでが Observer パターンなのか、かについて触れておく必要があると思います。
Observer パターンの大きなバリエーションの 1 つとしては、push モデルの Observer パターンなのか、pull モデルの Observer パターンなのか、というものがあります [1, p.318] [2, p.315]:
Observer パターンであることの要求
AspectJ を使った Observer に関するテクニックの種類
AspectJ を使った Observer の実装
package dp.observer; public interface Subject {}
import java.util.List; import java.util.Vector; import java.util.Iterator; public aspect SubjectAspect { private List Subject.observers = new Vector(); public void Subject.addObserver(Observer observer) { observers.add(o); } public void Subject.notifyObservers() { for(Iterator itr = observers.iterator() ; itr.hasNext(); ) { ( (Observer)itr.next() ).update(); } } public void Observer.update() {} }
package dp.observer; public class ConcreteSubject { private String state = "xxx"; public void setState(String newState) { this.state = newState; } public String getState() { return state; } private static aspect InnerSubjectAspect { declare parents : ConcreteSubject implements Subject; pointcut setState(Subject subject) : this(subject) && execution( void ConcreteSubject.setState(String) ); after(Subject subject) : setState(subject) { subject.notifyObservers(); } } }
package dp.observer; public class MyObserver implements Observer { public void update(ConcreteSubject subject) { System.out.println("update: " + subject.getState() ); } private static aspect InnerAspect { pointcut update(ConcreteSubject subject, MyObserver observer) : this(subject) && target(observer) && call(void Observer.update() ); void around(ConcreteSubject subject, MyObserver observer) : update(subject, observer) { observer.update(subject); } } }
package dp; import dp.observer.*; public class Client { public static void main(String[] args) { Observer observer = new MyObserver(); ConcreteSubject subject = new ConcreteSubject(); subject.addObserver(observer); subject.setState("yyy"); } }
update: yyy
実装 - その 2
public class Point { private int x; private int y; public Point(int x, int y) { this.x = x; this.y = y; } public void setX(int x) { this.x = x; } public void setY(int x) { this.y = y; } }
public class CoordinateObserver { public void update(Point point) { System.out.println("ok"); } }
public aspect PointAspect pertarget( subject() ) { pointcut subject() : target(Point); private List observers = new ArrayList(); public void Point.addObserver(CoordinateObserver observer) { PointAspect.aspectOf(this).addObserver(observer); } private void addObserver(CoordinateObserver observer) { observers.add(observer); } pointcut subjectChange(Point point) : target(point) && ( call( void Point.setX(int) ) || call( void Point.setY(int) ) ); after(Point point) returning : subjectChange(point) { for(Iterator itr = observers.iterator() ; itr.hasNext(); ) { ( (CoordinateObserver)itr.next() ).update(point); } } }
public class Client { public static void main(String[] args) { Point p = new Point(1, 1); p.addObserver( new CoordinateObserver() ); p.setX(5); // ok } }
実装 - その 3
段階 - その 1
import java.awt.*; public class Point { private int x; private int y; private Color color; public Point(int x, int y) { this.x = x; this.y = y; } public void setX(int x) { this.x = x; } public void setY(int x) { this.y = y; } public void setColor(Color color) { this.color = color; } }
public aspect PointAspect { protected interface Observer { public void update(Point point); } declare parents: CoordinateObserver implements Observer; declare parents: ColorObserver implements Observer; public void Point.addObserver(Observer observer) { } public void Point.notifyObservers(List observers) { for(Iterator itr = observers.iterator() ; itr.hasNext(); ) { ( (Observer)itr.next() ).update(this); } } }
public class CoordinateObserver { public void update(Point point) { System.out.println("CoordinateObserver"); } private static aspect Observer { private List observers = new ArrayList(); pointcut subjectChange(Point point) : target(point) && ( call( void Point.setX(int) ) || call( void Point.setY(int) ) ); after(Point point) : subjectChange(point) { point.notifyObservers(observers); } void around(CoordinateObserver observer) : args(observer) && call( void Point.addObserver(PointAspect.Observer) ) { observers.add(observer); } } }
import java.awt.Color; import java.util.*; public class ColorObserver { public void update(Point point) { System.out.println("ColorObserver"); } private static aspect Observer { private List observers = new ArrayList(); pointcut subjectChange(Point point) : target(point) && call( void Point.setColor(Color) ); after(Point point) : subjectChange(point) { point.notifyObservers(observers); } void around(ColorObserver observer) : args(observer) && call( void Point.addObserver(PointAspect.Observer) ) { observers.add(observer); } } }
public class Client { public static void main(String[] args) { Point p = new Point(1, 1); p.addObserver( new CoordinateObserver() ); p.addObserver( new CoordinateObserver() ); p.addObserver( new ColorObserver() ); p.addObserver( new ColorObserver() ); p.setX(5); p.setColor(Color.RED); } }
段階 - その 2
public abstract aspect PointObserver { protected List observers = new ArrayList(); abstract pointcut subjectChange(); after(Point point) : target(point) && subjectChange() { point.notifyObservers(observers); } }
public class CoordinateObserver { public void update(Point point) { System.out.println("CoordinateObserver"); } private static aspect Observer extends PointObserver { pointcut subjectChange() : call( void Point.setX(int) ) || call( void Point.setY(int) ); void around(CoordinateObserver observer) : args(observer) && call( void Point.addObserver(PointAspect.Observer) ) { observers.add(observer); } } }
public class ColorObserver { public void update(Point point) { System.out.println("ColorObserver"); } private static aspect Observer extends PointObserver { pointcut subjectChange() : call( void Point.setColor(Color) ); void around(ColorObserver observer) : args(observer) && call( void Point.addObserver(PointAspect.Observer) ) { observers.add(observer); } } }
段階 - その 3
public abstract aspect PointObserver { protected List observers = new ArrayList(); abstract pointcut subjectChange(); after(Point point) : target(point) && subjectChange() { point.notifyObservers(observers); } abstract pointcut observerType(); pointcut addObserver() : call( void Point.addObserver(PointAspect.Observer) ); void around() : observerType() && addObserver() { observers.add( thisJoinPoint.getArgs()[0] ); } }
public class CoordinateObserver { public void update(Point point) { System.out.println("CoordinateObserver"); } private static aspect Observer extends PointObserver { pointcut subjectChange() : call( void Point.setX(int) ) || call( void Point.setY(int) ); pointcut observerType() : args(CoordinateObserver); } }
public class ColorObserver { public void update(Point point) { System.out.println("ColorObserver"); } private static aspect Observer extends PointObserver { pointcut subjectChange() : call( void Point.setColor(Color) ); pointcut observerType() : args(ColorObserver); } }
段階 - その 4
public abstract aspect PointObserver pertarget( subject() ) { pointcut subject() : target(Point); protected List observers = new ArrayList(); abstract pointcut subjectChange(); after(Point point) : target(point) && subjectChange() { point.notifyObservers(observers); } abstract pointcut observerType(); pointcut addObserver() : call( void Point.addObserver(PointAspect.Observer) ); void around() : observerType() && addObserver() { observers.add( thisJoinPoint.getArgs()[0] ); } }
段階 - その 5
public abstract aspect PointObserver pertarget( subject() ) { pointcut subject() : target(Point); private List observers = new ArrayList(); abstract pointcut subjectChange(); after(Point point) : target(point) && subjectChange() { point.notifyObservers(observers); } abstract pointcut observerType(); pointcut addObserver() : call( void Point.addObserver(PointAspect.Observer) ); void around(PointAspect.Observer observer) : args(observer) && observerType() && addObserver() { observers.add( observer ); } }
実装 - その 4
public class Point { private int x; private int y; private Color color; public Point(int x, int y) { this.x = x; this.y = y; } public void setX(int x) { this.x = x; } public void setY(int x) { this.y = y; } }
public aspect CoordinateObserver pertarget( subject() ) { private pointcut subject() : target(Point); private List Point.observers = new ArrayList(); public static void observe(Point p) { p.addObserver( new Observer() ); } private void Point.addObserver(Observer observer) { observers.add(observer); } private void Point.notifyObservers() { for(Iterator itr = observers.iterator() ; itr.hasNext(); ) { ( (Observer)itr.next() ).update(this); } } pointcut subjectChange(Point point) : target(point) && ( call( void Point.setX(int) ) || call( void Point.setY(int) ) ); after(Point point) : subjectChange(point) { point.notifyObservers(); } protected static class Observer { public void update(Point point) { System.out.println("CoordinateObserver - " + this + " - " + point); } } }
実装 - その 5
段階 - その 1
public class Point { private int x; private int y; public Point(int x, int y) { this.x = x; this.y = y; } public void setLocation(int x, int y) { this.x = x; this.y = y; } }
public interface Observer { public void update(); }
public class Screen implements Observer { public void update() { System.out.println("Screen"); } }
public aspect PointAspect { private List Point.observers = new ArrayList(); public void Point.addObserver(Observer observer) { observers.add(observer); } private void Point.notifyObservers() { for(Iterator itr = observers.iterator() ; itr.hasNext(); ) { ( (Observer)itr.next() ).update(); } } private pointcut subjectChange(Point point) : target (point) && call( void Point.setLocation(int, int) ); after(Point point) returning : subjectChange(point) { point.notifyObservers(); } }
public class Client { public static void main(String[] args) { Point p = new Point(1, 1); p.addObserver( new Screen() ); p.setLocation(5, 5); } }
Screen
選択肢
段階 - その 2
public abstract aspect ObserverProtocol { protected interface Subject { } protected interface Observer { public void update(); } private List Subject.observers = new ArrayList(); public void Subject.addObserver(Observer observer) { observers.add(observer); } public void Subject.notifyObservers() { for(Iterator itr = observers.iterator() ; itr.hasNext(); ) { ( (Observer)itr.next() ).update(); } } protected abstract pointcut subjectChange(Subject subject); after(Subject subject) returning : subjectChange(subject) { subject.notifyObservers(); } }
public aspect PointAspect extends ObserverProtocol { declare parents : Point implements Subject; declare parents : Screen implements Observer; protected pointcut subjectChange(Subject subject) : target(subject) && call( void Point.setLocation(int, int) ); }
段階 - その 3
public aspect PointAspect extends ObserverProtocol { declare parents : Point implements Subject; declare parents : Screen implements Observer; private void Screen.update(Point point) { System.out.println("Screen: " + point); } private pointcut update(Point point, Screen screen) : this(point) && target(screen) && call(void Observer.update()); void around(Point point, Screen screen) : update(point, screen) { screen.update(point); } protected pointcut subjectChange(Subject subject) : target(subject) && call( void Point.setLocation(int, int) ); }
public class Client { public static void main(String[] args) { Point p = new Point(1, 1); p.addObserver( new Screen() ); p.setLocation(5, 5); } }
Screen: dp.observer.Point@187aeca
選択肢
段階 - その 4 : リファクタリング
public abstract aspect ObserverProtocol { // ... 省略 protected pointcut update() : call( void Observer.update() ); // ... 省略 }
public aspect PointAspect extends ObserverProtocol { // ... 省略 private pointcut updateCall(Point point, Screen screen) : this(point) && target(screen) && update(); void around(Point point, Screen screen) : updateCall(point, screen) { screen.update(point); } // ... 省略 }
段階 - その 5 : リファクタリング
public abstract aspect ObserverProtocol { // ... 前と同じ protected abstract pointcut subjectChange(); after(Subject subject) returning : subjectChange() && target(subject) { subject.notifyObservers(); } }
public aspect PointAspect extends ObserverProtocol { // ... 前と同じ protected pointcut subjectChange() : call( void Point.setLocation(int, int) ); }
段階 - その 6 : その他のメソッド
public class Point { private int x; private int y; private Color color; public Point(int x, int y) { this.x = x; this.y = y; } public void setLocation(int x, int y) { this.x = x; this.y = y; } public void setColor(Color color) { this.color = color; } }
public aspect PointAspect extends ObserverProtocol { // ... 省略 protected pointcut subjectChange() : call( void Point.setLocation(int, int) ) || call( void Point.setColor(Color) ); }
public class Client { public static void main(String[] args) { Point p = new Point(1, 1); p.addObserver( new Screen() ); p.setLocation(5, 5); p.setColor(Color.RED); } }
Screen: dp.observer.Point@187aeca Screen: dp.observer.Point@187aeca
段階 - その 7 : 完成
public class Point { private int x; private int y; private Color color; public Point(int x, int y) { this.x = x; this.y = y; } public void setLocation(int x, int y) { this.x = x; this.y = y; } public void setColor(Color color) { this.color = color; } }
public abstract aspect ObserverProtocol { protected interface Subject { } protected interface Observer { public void update(); } private List Subject.observers = new ArrayList(); public void Subject.addObserver(Observer observer) { observers.add(observer); } public void Subject.notifyObservers() { for(Iterator itr = observers.iterator() ; itr.hasNext(); ) { ( (Observer)itr.next() ).update(); } } protected pointcut update() : call( void Observer.update() ); protected abstract pointcut subjectChange(); after(Subject subject) returning : subjectChange() && target(subject) { subject.notifyObservers(); } }
public aspect PointAspect extends ObserverProtocol { declare parents : Point implements Subject; declare parents : Screen implements Observer; private void Screen.update(Point point) { System.out.println("Screen: " + point); } private pointcut updateCall(Point point, Screen screen) : this(point) && target(screen) && update(); void around(Point point, Screen screen) : updateCall(point, screen) { screen.update(point); } protected pointcut subjectChange() : call( void Point.setLocation(int, int) ) || call( void Point.setColor(Color) ); }
public class Client { public static void main(String[] args) { Point p = new Point(1, 1); p.addObserver( new Screen() ); p.setLocation(5, 5); p.setColor(Color.RED); } }
実装 - その 6
文献 [3] をもとに実装。
public class Subject { private String state; public void setState(String state) { this.state = state; } public pointcut stateSeted(Subject subject) : call( void Subject.setState(String) ) && target(subject); }
public aspect ConcreteObserver { before(Subject subject) : Subject.stateSeted(subject) { System.out.println(subject); } }
public class Client { public static void main(String[] args) { Subject subject = new Subject(); subject.setState("xxx"); } }実行結果:
dp.observer.Subject@e53108
実装 - その 7
段階 - その 1
public class Subject { private String state; public void setState(String state) { this.state = state; } }
public class ConcreteObserver { public void update(Subject subject) { System.out.println(subject); } }
public aspect ObserverMapping pertarget( target(ConcreteObserver) || target(Subject) ) { private ConcreteObserver observer; private Subject subject; public void ConcreteObserver.observe(Subject subject) { ObserverMapping.aspectOf(this).subject = subject; ObserverMapping.aspectOf(subject).observer = this; } before() : call( void Subject.setState(String) ) { ConcreteObserver observer = ObserverMapping.aspectOf( thisJoinPoint.getTarget() ).observer; observer.update( ObserverMapping.aspectOf(observer).subject ); } }
public class Client { public static void main(String[] args) { Subject subject = new Subject(); ConcreteObserver observer = new ConcreteObserver(); observer.observe(subject); subject.setState("xxx"); } }実行結果:
dp.observer.Subject@1d9dc39
段階 - その 2 : リファクタリング
public aspect ObserverMapping pertarget( target(Subject) ) { private ConcreteObserver observer; private Subject subject; public void ConcreteObserver.observe(Subject subject) { ObserverMapping.aspectOf(subject).observer = this; ObserverMapping.aspectOf(subject).subject = subject; } before() : call( void Subject.setState(String) ) { this.observer.update( this.subject ); } }このままでの実装では、ある subject オブジェクトが observe されていないと、NullPointerException がスローされてしまうという問題点があります:
public class Client { public static void main(String[] args) { Subject subject = new Subject(); subject.setState("xxx"); // NullPointerException } }
段階 - その 3 : バグの修正
public aspect ObserverMapping pertarget( target(Subject) ) { private ConcreteObserver observer; private Subject subject; public void ConcreteObserver.observe(Subject subject) { ObserverMapping.aspectOf(subject).observer = this; ObserverMapping.aspectOf(subject).subject = subject; } private static boolean isObserved(Subject subject) { return ObserverMapping.aspectOf(subject).observer != null; } before(Subject subject) : target(subject) && call( void Subject.setState(String) ) && if ( isObserved( subject ) ) { this.observer.update( this.subject ); } }
段階 - その 4 :
現在の実装では、以下のコードで示しているように、1 つの observer オブジェクトが複数の subject オブジェクトの状態を観察することをサポートしています:
public class Client { public static void main(String[] args) { Subject subject1 = new Subject(); Subject subject2 = new Subject(); ConcreteObserver observer = new ConcreteObserver(); observer.observe(subject1); observer.observe(subject2); subject1.setState("xxx"); subject2.setState("yyy"); } }
dp.observer.Subject@19189e1 dp.observer.Subject@1f33675しかし、ある subject オブジェクトが複数の observer オブジェクトによってその状態を観察できるような機能はサポートしていません:
public class Client { public static void main(String[] args) { Subject subject = new Subject(); ConcreteObserver observer1 = new ConcreteObserver(); ConcreteObserver observer2 = new ConcreteObserver(); observer1.observe(subject); observer2.observe(subject); subject.setState("xxx"); } }
dp.observer.Subject@19189e1この要求に対して、すぐに思い浮かぶ単純な解決策としては、リストを使うというものがあります:
import java.util.*; public aspect ObserverMapping pertarget( target(Subject) ) { private List observers = new ArrayList(); private Subject subject; public void ConcreteObserver.observe(Subject subject) { ObserverMapping.aspectOf(subject).observers.add( this ); ObserverMapping.aspectOf(subject).subject = subject; } before() : call( void Subject.setState(String) ) { for(Iterator itr = observers.iterator(); itr.hasNext();) { ( (ConcreteObserver)itr.next() ).update(subject); } } }
実装 - その 8
public class Point { private int x; private int y; public Point(int x, int y) { this.x = x; this.y = y; } public void setLocation(int x, int y) { this.x = x; this.y = y; System.out.println("setLocation - [" + x + ", " + y + "]"); } }
import instancelevel.InstanceLevelAspect; public aspect PointObserver extends InstanceLevelAspect { public static PointObserver newInstance() { return null; }; public void observe(Point p) { addObject(p); } after() : call( void Point.setLocation(int, int) ) { System.out.println("updated - " + this); } private static aspect TargetCheckImpl extends TargetCheck { protected pointcut adviceTarget() : target(Point); } }
public class Main { public static void main(String[] args) { Point p = new Point(1, 2); p.setLocation(2, 3); PointObserver o1 = PointObserver.newInstance(); PointObserver o2 = PointObserver.newInstance(); o1.observe(p); p.setLocation(10, 20); o2.observe(p); p.setLocation(100, 200); } }実行結果:
setLocation - [2, 3] setLocation - [10, 20] updated - aj.PointObserver@9931f5 setLocation - [100, 200] updated - aj.PointObserver@9931f5 updated - aj.PointObserver@1f1fba0
参考文献
更新履歴
todo