アスペクトの作成方法 AspectJ

インタータイプ宣言

インタータイプ宣言の概要

インタータイプ宣言は,AspectJ などが提供する, 「クラスに新たなメソッド,フィールド等を追加する」仕組みを指します.

複数のクラスに共通のメソッドやフィールドを持たせたいとき, 対象のクラス群から実装の一部を分割しておきたいときに使用されます.

名称にはいくらか変種があり,以下にバリエーションを挙げておきます. すべて同じ概念を指しています.

  • インタータイプ宣言,型間宣言 (Inter-Type Declaration)
  • インタータイプ定義 (Inter-Type Definition)
  • Introduction
    • AspectJ の古いバージョンでわれていた呼び名です.今はもうほとんど使われていませんが,人によっては使っていることがあります.

"静的な" 横断

インタータイプ宣言は,あるクラスに新たなメンバーを (クラスの外部から)追加することができます.

構造に対して影響を与えますが, プログラムの振る舞い自体には直接の影響を与えません (動的束縛によって呼び出されるメソッドを追加するか, 追加したメンバーを適切に呼び出すコードを追加する必要があります).

このような,アスペクトの結合時に解決される特性から, インタータイプ宣言は「静的な横断(static crosscutting)」と呼ばれ, ポイントカット+アドバイスによる実行時の「動的な横断(dynamic crosscutting)」 と区別されます.

使い道

AspectJ の場合,ポイントカット+アドバイスで処理を行うときに 必要なデータをオブジェクトに持たせておきたい場合に使われます.

複数のクラスが強く関連したメソッドを持つ場合 (Composite パターンなどで相互再帰呼び出しを起こす場合など)に それらの一連のメソッドを1箇所にまとめたい場合にも使うことができます.

また,インタフェース単体では提供できない デフォルトの実装(とそれに必要なフィールド定義)を アスペクトとして記述することができます.

そのほか,何らかの事情で変更したくないクラスに実装を追加したり, 1つの巨大なクラスを複数のアスペクトに分割して整理する といった使い方も可能です.

AspectJ におけるインタータイプ宣言の記法

インタータイプ宣言の記法は, 通常のメソッドやフィールドの宣言と基本的には同じです. メソッドやフィールドの名前の前に "クラス名." が付加されているところだけが違います.

記法は AspectJ/簡易リファレンスとしてもまとめています.

計算結果を記憶するアスペクトの例

この例では,Value クラスの calcValue というメソッドの計算結果を 保持するためのフィールドを Value クラスに追加し, 計算結果が保持されているときは, その保持した値を代わりに返す (calcValueメソッドの呼び出しを実際には行わない)ようにしています.

// 計算結果の値をオブジェクトにキャッシュするアスペクト
aspect CachingAspect {

  // キャッシュが有効かどうか保存するフィールドを Value クラスに追加
  private boolean Value.is_already_calculated = false;

  // キャッシュされる値を追加
  public int Value.cached_value = 0;

  // v.calcValue() 呼び出しを捕まえて,キャッシュがあればそれを返す
  int around(Value v): target(v) && call(int v.calcValue()) {
    // もしキャッシュが有効なら,キャッシュ値を返す
    if (v.is_already_calculated) return v.cached_value;
    else {
      v.cached_value = proceed(v);     // 本来の計算を実行し,値を保存
      v.is_already_calculated = true;  // キャッシュを有効に設定
      return v.cached_value;           
    }
  }

  // キャッシュされている値を出力するためのメソッドを追加
  public String Value.toDebugString() {
    if (this.is_already_calculated) {
      return this.toString() + "(cached=" + this.cached_value +")";
    } else return this.toString();
  }

}

インタフェースと実装を追加する例

インタータイプ宣言の機能の1つとして,インタフェースの追加が提供されています.

この機能を使うと,複数のクラスにメソッドやフィールドを追加することも可能です. Programming Guide には,次のような例が掲載されています.

aspect A {
  private interface HasName {}
  declare parents: (Point || Line || Square) implements HasName;

  private String HasName.name;
  public  String HasName.getName()  { return name; }
}

この例では,declare parents という文が,インタフェースの追加を行っています. インタフェースを追加したい クラスとして「Point または Line または Square」というように マッチ条件を記述しています. この記述をうまく使うことで,特定の名前のパターンやアノテーションを 持ったクラス群に対して共通のインタフェースやメソッドを追加することもできます.

インタータイプ宣言されたメンバーの扱い

インタータイプ宣言されたフィールドやメソッドは, 可視性なども含めて,本来のクラスに宣言されている場合と まったく同等に扱われます.

アスペクトの結合時にクラス側の情報が書き換えられることになるので, クラス側のコードからこれらのメンバーを参照することもできます.


トップ   差分 バックアップ リロード   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2007-01-04 (木) 09:42:27 (4283d)