アスペクト指向言語のための 独立性の高いパッケージシステム 今吉竜之介 柳澤佳里 千葉滋 東京工業大学
アスペクト指向言語(AOP) 横断的関心事を分離して記述可能 アスペクト指向では、 updateを呼び出すコードは 一箇所のみ オブジェクト指向の場合 アスペクト指向の場合 : move(); update(); aspect UpdateAspect { pointcut pc() : call(void Point.move()); after() : pc() { update(); } } moveの直後にupdateを呼び出す : move(); update(); class Point { move() { ... } } アスペクト指向では、 updateを呼び出すコードは 一箇所のみ 拡張、修正が容易 : move(); update(); updateを呼び出すコードが複数個所に散在
例:描画ソフトの共同開発 ソフト全体をモジュールとして分割し、 それぞれ異なる開発者が作成 開発者によってはアスペクトを最初から利用 モジュール内でのプログラムの見通しが向上 E.g.) observer pattern、トランザクション処理など FigureModule 図形を管理するパッケージ 図形の状態変化を 通知するために AOP を使用 WindowModule ウィンドウ本体を管理する パッケージ
FigureModule それぞれの図形は色を持ち、setColorで変更する class Point { void setColor(Color c) { ... } } class Circle { class Panel { aspect UpdateScreen { pointcut pc() : execution(void *.setColor(..)); after() : pc() { Screen.update(); } FigureModuleの Screenとの結合テストは上手くいく 色の変更時を指定 色の変更直後に 再描画
コンポーネント毎の開発が終わったので、次はそれらを結合 WindowModule Frameクラスを実装 フレームの色を変更するメソッド名がsetColor class Frame { void setColor(Color c) { /* フレーム色を変更 */ Screen.update(); } } フレーム色の 変更直後に再描画 WindowModuleのScreenとの結合テストは上手くいく コンポーネント毎の開発が終わったので、次はそれらを結合
結合時に不具合 フレームの色を変更すると画面がちらつく! 名前の予期せぬ一致 クラス側のupdate直後に アスペクト側で再びupdateを class Point { void setColor(Color c) { ... } } aspect UpdateScreen { pointcut pc() : execution(void *.setColor(..)); } 名前の予期せぬ一致 class Frame { void setColor(Color c) { /* フレーム色の変更 */ Screen.update(); } クラス側のupdate直後に アスペクト側で再びupdateを 実行してしまう
fragile pointcut問題 アスペクトがプログラムに予期しない影響を与える ポイントカットで指定した条件と aspect class fragile pointcut ポイントカットで指定した条件と 名前が偶然一致すると発現 ポイントカット指定でワイルドカードを使用すると危険 class
AspectJのセマンティクスの修正を提案 アドバイスの織り込み範囲を変更 同一のパッケージのみにアドバイスを織り込む パッケージ外のジョインポイントは選択不可 現状のAspectJの動作では間違いやすい fragile pointcut 問題 class aspect class ×
修正の効果: モジュールの独立性が向上 パッケージ単位で再利用性が向上 各パッケージの挙動が予測しやすい 再利用の前後でプログラムの挙動が変化しない プログラムとアスペクトが切り離されない 各パッケージの挙動が予測しやすい プログラムがシステムの別の部分にあるアスペクトの影響を 受けない ワイルドカードによる誤った選択を防ぐ 名前の条件が一致しても、無関係なモジュールには無影響 無関係なクラスにあるメソッド名などを気にせずに開発可能 図または絵などを挿入すべきとは思ったのですが・・・
修正による弊害: パッケージをまたがるアスペクト 重複するアスペクトが必要になる場合が存在 : move(); 例: ロギング用のアスペクト aspect LoggerAspect { pointcut pc() : call(void Point.move()); before() : pc() { /* ログの取得 */ } : move(); class Point { move() { ... } } : move(); パッケージの数だけ重複
修正案へ追加: 織り込みについての拡張ルール インターフェイスに対するアドバイス 公開ポイントカット パッケージをまたがるアスペクトへの対応 アスペクトを用いた observer pattern などにも対応 明示的な記述により、パッケージをまたがる アスペクトを可能に 予期せぬ織り込みを引き起こさないよう留意して設計 (仮)は少なくとも本番では除去するつもりです
インターフェイスに対するアドバイス 特定のクラスメンバへのパッケージ外からの アクセス全てに作用するアドバイス aspect アスペクトを一箇所に記述可能 例) 外部からのアクセスの前に 必ずassertionを実行 : move(); aspect : move(); class Point { move() { ... } } パッケージ内のクラスメンバへの call,set,getに [&& !within(自パッケージ名)] を追加で記述すると反映 : move();
この拡張によって重複するアスペクトを回避 ロギング用のアスペクトを可能に aspect LoggerAspect { pointcut pc() : call(void Point. move()) && !within(FigureComponent); before() : pc() { /*ログの取得*/ } } : move(); : move(); public class Point { public void move() { ... } } : move(); この拡張によって重複するアスペクトを回避
予期せぬ織り込みの回避 明示的に記述される必要があるため、予期せぬ織り込みにはならない [&& !within(自パッケージ名)]を付加しない限り、 この拡張ルールは反映されない 選択されるジョインポイントはパッケージ外 パッケージ内メソッドの呼び出し元 予期せぬメソッド呼び出しが選択され、アドバイスが織り込まれる可能性は少ない 。。。だから予期せぬ織り込みはない、という文に直す 一番下のは、呼び出し元のパッケージ側の視点に立っても予期せぬ織り込みにはならないということを言いたいと思っています。
修正によるもうひとつの弊害: observerクラスとアスペクトの隔絶 aspect UndoAspect { pointcut pc() : execution(void *.move()); before() : pc() { //undo のための履歴保存 } class Point { move() {...} } Undo Undoは別のモジュールの処理なので、UndoAspectもUndo側に置きたい 情報隠蔽は公開ポイントカットの利点だと考えました。 情報隠蔽の機構が無いことはAspectJ自体の問題点であり、我々の方法による弊害ではないからです。 むしろ、公開ポイントカットがあると構造体を使うことと同様、その解決の一助になると考えています。 しかしアスペクトをUndo側のパッケージに置いても問題は解決されない class Point { move() { ... } } Undo × 別パッケージのイベントは選択できない
公開ポイントカット 抽象アスペクトが定義したポイントカットは 公開ポイントカットとなる 抽象 aspect class aspect 公開ポイントカットは子アスペクトのみ使用可能 子は親と同一のパッケージに属していると見なされる abstract aspect Parent { pointcut pc() : ... } 抽象 aspect class aspect Child extends Parent { before() : pc() {...} } aspect class
observerクラスとアスペクトを同一パッケージに記述可能に public abstract aspect UpdateAspect { pointcut pc() : execution(void *.move()); } public class Point { public void move() { ... } } aspect UndoAspect extends UpdateAspect { before() : pc() { // Undoのための履歴保存 } public class Undo { void set() { ... } } この拡張によって、パッケージ外からイベントを選択可能
公開ポイントカットの利点と妥当性 利点 妥当性 プログラムのイベントをパッケージ外部からも選択可能 フレームワークの設計に利用可能 例) アスペクト指向を用いた observer pattern の実装など フレームワークの設計に利用可能 親アスペクトで拡張ポイントを示し、子アスペクトでそのポイントを利用者が自由に拡張する 情報隠蔽の仕組みを提供 妥当性 公開側と継承側の両方に明示的な宣言が必要なため、 予期せぬ織り込みにはならない
システムの実装 AspectJ言語を拡張してセマンティクスを変更 abc(AspectBench Compiler)を 改造して実装 Aspect Info Advice Weaving を拡張 AspectJ言語を拡張してセマンティクスを変更 abc(AspectBench Compiler)を 改造して実装 全てのジョインポイントは、そこに 織り込まれるアドバイスのリストを保持 織り込む直前に、そのリストにある全ての アスペクトの情報をチェック 織り込み制限のルールに適していなければ、 そのアドバイスはリストから除外 全てのジョインポイントに対して同様に処理 インタータイプ宣言にも同様の処理
実験:AJHotDrawの修正 セマンティクスの変更により、アスペクトのソースコードがどのように変化するかを計測 重複するアスペクトの有無 記述不可能なアスペクトの有無 対象: AJHotDraw (ver 0.3) アスペクト指向で設計されたDrawing Tool コード数:41903行 / ファイル数:300個 本システム下でも正しく動作するようにアスペクトを修正 織り込み条件の変更により、オリジナルのプログラムと挙動が異なるため クラス側のソースコードは変更も移動も無し
実験結果 アスペクトのファイル数、パッケージ数が増加するが、 重複するアスペクトは無い AJHotDrawで記述不可能なアスペクトは無い 修正前 修正後 ・アスペクトを含むパッケージ数 ・アスペクトのファイル数 ・ソースコード上のアドバイスの数 ・インタータイプ宣言の数 ・織り込まれたアドバイスの数 3 10 5 31 44 7 17 単一アスペクトが複数ファイル、パッケージに分割された コードの重複は無し アスペクトのファイル数、パッケージ数が増加するが、 重複するアスペクトは無い AJHotDrawで記述不可能なアスペクトは無い 拡張ルールはAJHotDrawで使用されなかった observer pattern を実装するアスペクトは存在したが、 パッケージをまたいでいなかった
関連研究:AspectJの場合 AspectJではwithinを用いることで織り込みを制限 全てのポイントカットへの記述は手間がかかる 修正後のセマンティクスも、 ある程度までなら再現可能 全てのポイントカットへの記述は手間がかかる 記述を忘れたり、間違った条件を付けたりした場合にチェックする 機構が存在しない 本システムでは、暗黙のうちに正しい条件文が付加される aspect UpdateDisplay { pointcut pc() : execution(void *.setColor(..)) && within(FigureComponent.*); pointcut pc2() : execution(void *.move(..)) }
関連研究: Open Module [Aldrich et al. ’06] 公開したいジョインポイントを 任意に記述 ジョインポイントが一致すると 無関係でも選択される恐れ 本研究は名前付ポイントカットを 公開するため、予期しない選択は排除 アスペクトを含むようなパッケージの 再利用については考慮していない module FigureModule { class Point || Circle || Panel; friend DebuggingAspect; expose call(void Point.setColor(..); }
関連研究:ccJava [境ら ’06] アスペクトの織り込みに対応した「織り込みインターフェイス」を導入 パッケージ再利用に不向き 公開した部分のみ織り込み アドバイスもモジュールとして独立 パッケージ再利用に不向き 修正の際に、織り込みインター フェイスの分だけ手間が増加 w_interface xFig { pointcut set() : execution(void setColor(..)); import after set(); } w_interface xDraw { pointcut draw() : execution(void update()); export draw(); } weave { class Point implements xFig; class Display implements xDraw; connect(xDraw.draw, xFig.set); }
まとめ AspectJのセマンティクスの修正を提案 abcを改造して実装 AJHotDrawで実験 アスペクトは同一パッケージ内のジョインポイントのみ選択可能 パッケージの独立性の向上 拡張ルール アスペクトの記述力を保つためのルール パッケージの独立性を壊さないように留意 abcを改造して実装 AJHotDrawで実験 修正セマンティクス下で記述不能なアスペクトは無い 拡張ルールの使用は無い
今後の課題 セマンティクスの修正を適用した際の副作用の検証 拡張ルールの洗練 アドバイスやポイントカットの総数が 増加する可能性がある 拡張ルールの適用例を発見 拡張ルールの追加、修正 クラスがパッケージをまたいで継承している場合を考察 内部クラスへの対応を考察 など