2009年12月21日月曜日

AspectJ で using 的な構文を模倣してみる (3)

前に AspectJ で using ぽい構文を試してみた。任意のクラスの解放メソッドを自動的に実行するところまでやったが、今回はネストした using ブロックを試してみる。 以下のようなコードで、、、
public class Client {
   public static void main(final String[] args) throws Exception {
      new UsingA() { A r1 = new A("r1"); {
         new UsingB() { B r2 = new B("r2"); {
            new UsingA() { A r3 = new A("r3"); {
               r1.use("x");
               r2.use("y");
               r3.use("z");
            }};
         }};
      }};
   }
}
、、、自動的に r3, r2, r1 の順にリソースが解放されるようにしてみたい。(前回までのコードでは r1 が解放されず r3 が2度解放されてしまっていた。) 以下のように Stack を使って UsingAspect を修正。ついでにマルチスレッドで使えるように percflow(using()) をアスペクト定義に付与。
public abstract aspect UsingAspect<T, U> percflow(using()) {
   protected Stack<T> stack = new Stack<T>();
   pointcut settingField(T o): set (T+ U+.*) && args(o);
   pointcut using(): call(U+.new(..));

   after(T o): settingField(o) {
      this.stack.push(o);
   }
   after(): using() {
      if (!stack.empty()) endUsing(this.stack.pop());
   }
   protected abstract void endUsing(T o);
}
その他のクラスは、ほとんど自明だけど以下のようなものになる。
UsingA
public interface UsingA {
   public static aspect AspectA
         extends UsingAspect<A, UsingA> {
      @Override
      public void endUsing(A o) {
         if (null != o) o.dispose();
      }
   }
}
UsingB
public interface UsingB {
   public static aspect AspectB
         extends UsingAspect<B, UsingB> {
      @Override
      public void endUsing(B o) {
         if (null != o) o.close();
      }
   }
}
A
public class A {
   private final String name;
   public A(String name) {
      this.name = name;
   }
   public void dispose() {
      System.out.printf("%s disposed%n", this);
   }
   public void use(String str) {
      System.out.printf("%s used with \"%s\"%n", this, str);
   }
   public String toString() {
      return String.format("A(\"%s\")", this.name);
   }
}
B
public class B {
   private final String name;
   public B(String name) {
      this.name = name;
   }
   public void close() {
      System.out.printf("%s closed%n", this);
   }
   public void use(String str) {
      System.out.printf("%s used with \"%s\"%n", this, str);
   }
   public String toString() {
      return String.format("B(\"%s\")", this.name);
   }
}
実行すると以下のようなコンソール出力を得る。
A("r1") used with "x"
B("r2") used with "y"
A("r3") used with "z"
A("r3") disposed
B("r2") closed
A("r1") disposed
割と簡単に using のネストができた。一応、だんだんと実用的になってきた気がする。取りあえずテストコードで使い始めて、パフォーマンスなんかを中心にしばらく観察してから、問題が無ければプロダクトコードで使ってみるような流れで良いか。

0 件のコメント:

コメントを投稿