asatoの技術的な日常日記

「成長に最大の責任をもつ者は、本人であって組織ではない。自らと組織を成長させるためには何に集中すべきかを、自らに問わなければならない」  非営利組織の経営 - ピーター・ドラッカー

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

アスペクト指向デザイン: DI Aspect

ちゃんと分析できてないけど、書かないと忘れそうなので書く。

Dependency Injection (DI) とアスペクトの関係は?

学術的には、東工大の石川さんと千葉さんが論文を書いている。

 アスペクト指向プログラミングと Dependency Injection の融合

 Aspect-Oriented Programming beyond Dependency Injection

すでに彼らが分析しているのかもしれないけど、勉強のため考えてみる。


DI についての一般的な記事は、マーチンファウラさんの記事がある。

 Inversion of Control コンテナと Dependency Injection パターン


DI は基本的には、コンポーネント間の関係をどうやって管理するかに関するものだと思う。

Spring などのコンテナのやってることだけからいえば、コンポーネント間の関係を設定ファイルであらかじめて設定しておく。そして、そのファイルを基に、構成済み(依存性注入済み)のオブジェクトを取り出す。

Spring などの設定ファイルベースの DI は、一つのアプローチだと思う。もう一つのアプローチは、アスペクトを使うことだと思う。僕の後者の経験が少ないから、落とし穴にはまるかもしれないけど、考えてみる。

簡略化した実例から入ってみようと思う。Java でゲームを作っているとして、次のようなクラスがあるとする。


public class GameManager {
// ... ゲームの管理を行う。
// 現在のゲームの状態(タイトル画面、ゲーム中、ゲームオーバー)など。
}

public class AttackKeyHandler {

private Player player;
private GameManager gameManager;
private FPSManager fpsManager;
private KeyManager keyManager;

public AttackKeyHandler(Player player, FPSManager fpsManager, KeyManager keyManager) {
this.player = player;
this.gameManager = gameManager;
this.fpsManager = fpsManager;
this.keyManager = keyManager;
}

public void handle(KeyEvent e) {
// ... 攻撃のキーが押されたときの処理を行う
}
}



一つは、ゲームの管理を行うクラス。設計的には、このクラスは、ゲーム中に一つだけあると仮定して問題ない。

もう一つは、ユーザが攻撃のときのキーを押したときの処理を行う。この処理を行うためには、色々な管理クラスへのアクセスが必要だとする。

ここでの問題点は、コンストラクタの引数が比較的多いということ。もちろん、うまく設計されてないからこうなっているとも考えられる。たとえば、管理クラスを管理するもっと多きな管理クラスを作れば、引数の数は減る。

あるいは、GameManager や FPSManager のオブジェクトがゲーム中に一つだけ存在しているとするなら、これらのクラスに Singleton パターンを適用して handle メソッド内で、これらのクラスにアクセスするようにすればいいかもしれない。

とはいえ、テスト容易性の観点と経験から言うと、あまり容易に Singleton を適用するのは避けたい。

したがって、Spring などの DI のアプローチが思い浮かぶ。しかし、設定ファイルのアプローチは、ちょっと避けたい。理由の一つは、Spring の ApplicationContext を使って、設定済みのオブジェクト(この例では、AttackKeyHandler)を得るのがよさように思えない。


そこで、アスペクトを使えばどうかな、と考えてみる。たとえば次のようにする



@Injectable
public class AttackKeyHandler {

private Player player;
private @Injected GameManager gameManager;
private FPSManager fpsManager;
private KeyManager keyManager;

public AttackKeyHandler(Player player, FPSManager fpsManager, KeyManager keyManager) {
this.player = player;
this.fpsManager = fpsManager;
this.keyManager = keyManager;
}

public void handle(KeyEvent e) {
// ...
}
}



@Injectable と @Injected アノテーションを使って、どのフィールドが自動的に設定されるのかを指定している。

アスペクトのコードはこんな感じ。



public aspect DependencyInjection {

declare parents : (@Injectable *) implements InjectableType;
}

public aspect GameManagerDependencyInjection {

private GameManager gamaeManager; // 注入されるオブジェクト

// 注入を要求するオブジェクトが生成されたとき
after() returning(InjectableType o) : call( (@Injectable *).new(..) ) {
inject(o);
}

public void setGameManager(GameManager gamaeManager) {
this.gamaeManager = gamaeManager;
}

private void inject(InjectableType o) {

for(Field f : o.getClass().getDeclaredFields()) {

if ( f.isAnnotationPresent(Injected.class) ) {

if ( f.getType().isAssignableFrom(GameManager.class)) {

boolean access = f.isAccessible();
try {
f.setAccessible(true);
f.set(o, gamaeManager);

} catch(IllegalAccessException e) {

throw new DependencyInjectionException(e);
} finally {
f.setAccessible(access);
}
}
}
}
}
}
public class ....
private void initGame() {
// ... その他の初期化
GameManagerDependencyInjection.aspectOf().setGameManager(gameManager);
}
}



GameManagerDependencyInjection アスペクトは、最低限の動く実装しかしていないけど、ライブラリ化するならもっと一般化はできると思う。


利点は:
-設定ファイルのアプローチと比べて、比較的軽量。

欠点は:
-テストが容易でなくなるかもしれない。アスペクトとテスト容易性の関係は、まだまだ研究が必要な分野。

-コードの理解容易性の低下。AJDT などの アドバイスがどこに適用されているのかが分かる開発環境がないと、オブジェクト振る舞いが分かりにくいかもしれない。Spring などの場合は、ApplicationContext とかを使って設定済みのオブジェクトを得るので、DI しているってことが明示的だと思う。


欠点が多いように思えるけど、なぜかアスペクトのアプローチを今のところ採用してしまう現状。
スポンサーサイト

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバックURLはこちら
http://asatohan.blog77.fc2.com/tb.php/13-3a80e31e
この記事にトラックバックする(FC2ブログユーザー)

FC2Ad

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。