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