まず、AOPが考案された背景とその概要について説明する。通常、システムはいくつかの関心事(Concern)に分割することができる。それぞれの関心事は、プログラミング言語が提供する仕組みを利用して、モジュールとして実装することになる。ここで言うモジュールとは、関数やメソッドに相当する。オブジェクト指向プログラミングであればクラスなどがモジュールに当たる。システム全体は、これらのモジュールを組み合わせることで実現する。
しかし、従来の関数やクラスなどをモジュール化の単位とすることでは、うまく分割しきれない関心事も存在する。オブジェクト指向など、既存のモジュール化技術で設計されたプログラムに、そのようなうまく分割できない関心事を追加で実装すると、クラス階層をまたがって、さまざまな個所に同じような処理を担うコード(以下、コード断片)が散在してしまうことがある。このような関心事を「横断的関心事(Cross-cutting Concern)」と呼ぶ。具体的には、プログラムのデバッグ時に変数の値を出力させたり、システムの状態監視を目的として何らかの値を記録したりするためのロギング処理や、認証/アクセス制御といったセキュリティ処理などが横断的関心事として知られている。横断的関心事はソースコードのモジュール性を下げるので、ソースコードの保守性や信頼性、開発効率を低下させる要因となる。
ロギングを例にとると、ログファイルを開いたりログメッセージを書き込んだりする機能自体は、関数やクラスとして切り分けることができる(すなわち、モジュール化できる)。しかし、実際にどのタイミングでメッセージを書き込むべきかは、システムを構成する各モジュール内の必要な個所に、ロギングのための命令(ログメッセージを書き込む関数の呼び出し)を書き加えて示す必要があった(図1)。
そして、横断的関心事は、一般的なソフトウエアに特有の問題ではない。ハードウエアの機能を記述したソフトウエア(つまりは設計記述)においてもしばしば発生する。例えば、ハードウエアが異常な状態に陥っていないかどうかをテストするための機能を提供する場合、さまざまなモジュールにチェック用のコード断片を挿入しなければならないことがある。あるいは、内部の状態を参照するために信号線を外部に引き出したりするコード断片をあちこちに記述しなければならないケースもある。また、シミュレーションを高速化するために、ところどころを抽象化した実装に一時的に差し替えるといったことも行われる。
しかし、ソースコードを直接書き換えてしまうことは、本来の挙動がわかりにくくなったり、修正もれなどのヒューマンエラーを起こしたりするので望ましくない。マクロを利用すれば毎回手作業によってソースコードを書き換える必要はなくなるが、そうしたコード断片が最終的にハードウエアに反映されるわけではなく、本質的でない記述がソースコード上に多数残るため、保守性の面で問題がある。そして、リスト5、リスト6の例で示した対処法によってアサーションを実施しようとすれば、ダミー変数を用いた同様のコードがほかのモジュールにも遍在することになる。すなわち、これも横断的関心事である。
この横断的関心事の問題を解決する技術がAOPである。AOPを一言で説明すると、横断的関心事をモジュール化する(切り分けて実装する)技術だと言える。AOPでは、クラスとは別のモジュール化単位である「アスペクト」が提供される。このアスペクトを用いることにより、横断的関心事をモジュール化することができる。アスペクトはコード断片である「アドバイス(Advice)」と、そのコード断片を埋め込む個所を指示する「ポイントカット(Pointcut)」の組から成る(図2)*4)。上述したように、モジュール化自体は通常の関数やクラスを使って行うことが可能だが、AOPではコード断片を埋め込む個所を外部からアスペクトによって任意の位置に指定でき、埋め込み自体はAOPの処理系が行う。このような仕組みにより、システムを構成するモジュール内には変更を加えることなく、横断的関心事を分離することが可能になるのだ。
従来、AOPは一般的なソフトウエアを対象としていたが、筆者らは、ハードウエアの横断的関心事に対してもAOPが効果的であるか否かを検討している。特に、AOPを利用して、高位設計向けのアサーションベース検証を可能にしようと試みている。ハードウエアを対象とした場合に、AOPに必要な追加機能を検討するための研究の土台として、SystemCをベースとしたハードウエア記述用アスペクト指向言語「ASystemC」を設計/開発した。ASystemCはSystemCのAOP拡張言語(処理系も含む)と言えるものである。SystemCのソースコードとアスペクトを入力として受け取り、アスペクトの指示に従って書き換えた新たなソースコードを出力する。これによって、ロギングや、回路の記述の動的解析、最適化、テスト用コードの自動生成などの横断的関心事を個々のモジュールとして実装することができる。
ASystemCで採用しているAOPのパラダイムは、Javaの既存AOP拡張言語である「AspectJ」をベースとしている。通常のSystemCの構文に加え、関数呼び出しや関数定義、変数代入などのイベントを指示するポイントカットと、その個所に挿入すべきコード断片をアドバイスとして持つアスペクトをサポートする。
ASystemCのポイントカットは、ソースコード中の個所を指定する。ASystemCでは、表2に示した5種類のポイントカットを定義している。例えば、「call(func)」と書くと「関数funcを呼び出す時点」という意味になる。fileだけはASystemC独自のポイントカットだが、ほかのポイントカットはAspectJで提供されているものと同等である。
AspectJには、アスペクト、アドバイス、ポイントカットのほかに、もう1つ「ジョインポイント(Joinpoint)」という概念がある。ジョインポイントはプログラムの動作中の動的な時点を定義するものであり、ポイントカットは「ジョインポイントの集合を指定する論理式」であると定義されている。それに対し、ASystemCではジョインポイントの概念は用いない。ポイントカットは単純に「ソースコード上で特定の特徴を持つ個所を指示する構文」であるとしている。これは、AspectJが汎用言語であるJavaを対象としたものであるのに対し、回路設計用の言語であるSystemCでは、プログラムの個々の構文が回路内の部品と静的に対応するので、動的な時点を指定したいケースが少ないと考えたことによる。
アドバイスは、種別、ポイントカット、コード断片の3つの組から成る。アドバイスの種別によって、ポイントカットで指定した個所に対してコード断片をどのように適用するかが決まる。ASystemCは、表3に示す3種類のアドバイスを提供する。これらはAspectJのアドバイスの記述/意味とまったく同じである。
リスト7にアドバイスの記述例を示した。これは、「関数funcの呼び出しを行う個所すべてに対し、その直後に『puts("return func!");』というロギング用のコードを挿入する」という意味になる。
アスペクトはアドバイスの集合から成る。例えば、リスト8のアスペクトは、ファイルfoo.cの先頭に「int counter = 0;」というコードを挿入し、さらに、関数funcの呼び出しを行った直後に「counter++;」というコードを挿入するという意味になる。すなわち、このアスペクトは、関数funcからリターンした回数をカウントするカウンタを適用する役割を果たす。
※4…この部分は、AspectJが採用するポイントカット、アドバイスの機構について説明している。AspectJはJavaのAOP拡張であり、AOPのデファクトスタンダードとして利用されている処理系である。これ以外の機構によって横断的関心事をモジュール化するAOPも存在するが、それについては本稿では触れない。
Copyright © ITmedia, Inc. All Rights Reserved.