Advice Reusability and Named Advice

名前つきアドバイスはなぜ必要か? たとえば:


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 y) {
this.y = y;
}
}

public aspect PointLogging {

pointcut methods() :
call( void Point.setX(int) ) || call( void Point.setY(int) );

before() : methods() {
// log
}
}

とかが再利用可能な形で配布されてたとする。


AspectJ では、inter-type 宣言で外部からクラスの構造を変更できるので以下のように Point クラスを拡張できる:


public aspect PointExtension {

private String Point.color;

public void Point.setColor(String color) {
this.color = color;
}
}

しかし、どうやって、PointLogging アスペクトを setColor メソッドの振る舞いをログするように変更するのか? 現在の AspectJ では、どうしようもない気がする。つまり、before() : methods() { ... } の部分は再利用可能でない。


問題は、以下の二点から考えられると思う:

  • PointLogging アスペクトが変更(setColor メソッドなどが追加されたときにログが期待通りに動作しない)を予期しないような悪い設計になっていた。
  • AspectJ が適切なメカニズムを提供していないのが悪い。


前者の視点からは、たとえば(コンパイルしてないので何が不具合があるかもしれないけど)、


public abstract aspect AbstractPointLogging {

pointcut abstract methods();

before() : methods() {
// log
}
}
public aspect PointLogging extends AbstractPointLogging {
pointcut methods() :
call( void Point.setX(int) ) || call( void Point.setY(int) );
}

みたいに設計されていれば:

public aspect PointExtensionLogging extends AbstractPointLogging {

pointcut methods() : call( void Point.setColor(String) );
}

というふうにできたかもしれない(コンパイルしてないので誤っている可能性あり)。


後者の視点からは、もし、アドバイスに名前があるのであれば:


public aspect PointLogging {

pointcut methods() :
call( void Point.setX(int) ) || call( void Point.setY(int) );

before set() : methods() {
// log
}
}

public aspect PointExtension {

private String Point.color;

public void Point.setColor(String color) {
this.color = color;
}

PointLogging.set() : // アドバイスの指定
pc() || // オリジナルのポイントカット
call( void Point.setColor(String) ); // 追加するポイントカット

}

というふうにできるかもしれない。


あるいは、アドバイスをポイントカットが関連付けられた特殊なメソッドだと考えると:


public aspect PointLogging { // 変更なし

pointcut methods() :
call( void Point.setX(int) ) || call( void Point.setY(int) );

before set() : methods() {
// log
}
}

public aspect PointExtension {

private String Point.color;

public void Point.setColor(String color) {
this.color = color;
}

before setColorCall() : call( void Point.setColor(String) ) {
PointLogging.aspectOf().set(); // アドバイスの直接呼出し
}
}

ともできるかもしれない。


とすると、単にアドバイス内から直接メソッドを呼べば名前つきアドバイスなんていらないのでは、と思うかもしれない:


public aspect PointLogging { // 変更なし

pointcut methods() :
call( void Point.setX(int) ) || call( void Point.setY(int) );

before() : methods() { // 普通のアドバイス
log();
}

public void log() {
// log
}
}

public aspect PointExtension {

private String Point.color;

public void Point.setColor(String color) {
this.color = color;
}

before() : call( void Point.setColor(String) ) {
PointLogging.aspectOf().log(); // メソッドの直接呼出し
}
}


ということで微妙な結論に。