2009年12月13日日曜日

AspectJ design pattern: Participant

Aspectj in Action: 第一版に載ってた、Participant パターンについて。
class ClassA {
   public void foo() {
      System.out.println("ClassA.foo() called");
   }
   public void bar() {
      System.out.println("ClassA.bar() called");
   }
}
public class App {
   public static void main(String[] args) {
      ClassA a = new ClassA();
      a.foo();
      a.bar();
   }
}
上のコードに作用して、以下のように星印をつけてコンソール出力を修飾する Star アスペクトがあるとする。
アスペクト適用なしStar アスペクト適用
ClassA.foo() called
ClassA.bar() called
 ⇒ 
★ClassA.foo() called
ClassA.bar() called
上の例では★がついているのは ClassA.foo() だが、後々の拡張性を考えて、どのメソッドが★印修飾されるかについて、アスペクト定義の側にはハードコードしたくない。また、後で★印修飾を適用するメソッドは、ジョインポイントにマッチさせるためのシグネーチャの制約(例えば命名規約だとか)をつけたくない。こういう状況で、Participant パターンが適用できる。 まず以下のように抽象アスペクトを定義する
public abstract aspect Star {
   public abstract pointcut needStar();
   Object around() : needStar() {
      System.out.print("★");
      return proceed();
   }
}
次に、ClassA のクラス定義内で具象アスペクトを定義する。
class ClassA {
   public static aspect StarForA extends Star {
      public pointcut needStar(): call(void ClassA.foo());
   }
   public void foo() {
   ...略
}
こんな風にして、上図右のような★印修飾がついたコンソール出力が得られる。 さて、後でClassB を追加して、App.main() を以下のように変更したとする
public static void main(String[] args) {
   ClassA a = new ClassA();
   ClassB b = new ClassB();
  
   a.foo();
   a.bar();
   b.baz();
}
ここで、メソッド baz() にも★印修飾を与えたい場合、以下のように書けばよい。
class ClassB {
   public static aspect StarForB extends Star {
      public pointcut needStar(): call(void ClassB.baz());
   }
   public void baz() {
      System.out.println("ClassB.baz() called");
   }
}
こうすれば、Starアスペクトへの変更も、baz() へのシグネーチャ制約も無く、以下のような出力が得られる。
★ClassA.foo() called
ClassA.bar() called
★ClassB.baz() called
■所感
  • GoF の Template パターンでは、変更を与えるポイントが決まっていて、そのポイントでの振る舞いをカスタマイズする。一方、この Participant パターンでは、ある一定の振る舞いの変化を、どこに適用するかについてカスタマイズする。つまり Template パターンとParticipant パターンで再利用部分とカスタマイズ部分が逆になっているが、これが OOP と AOP の典型的な違いのような気がする。
  • 具象側のアスペクトでポイントカットを定義する代わりに、アノテーション を使うやり方もある。上の例では、内部アスペクトを定義してクラス単位のジョインポイント管理にしていたが、アノテーションを使うとメソッド単位で アスペクトに参加するメソッドを宣言することになり、より粒度の細かい管理になる。
    pointcut p() : execution(@Hoge * *.*());

0 件のコメント:

コメントを投稿