C++ 版 DI コンテナ

id:makotanさん、id:Akaponさんに触発されて、
簡単なプロトタイプを作ってみました。

例題

例題

簡単な例題を使って説明します。Client、Supplier 2つのインタフェースがあり、それぞれ ClientImpl、SupplierImpl で実装します。Main から Client の foo() が呼ばれ、ClientImpl から Supplier の bar() が呼ばれます。
ただし、ClientImpl は Supplier の実装クラスが何なのか直接知りたくありません。つまり、ClientImpl の中で SupplierImpl を new せずに、外部から設定できるようにします。また、SupplierImpl の属性 message も外部から設定できるようにしたいと思います。
このような設計を DI (Dependency Injection) と呼びます。DI コンテナを使用することが DI ではないのでお間違いなく。

クラスの説明

C++ 版 DI コンテナを構成するクラスを説明します。

  • Container
    DI コンテナの本体です。名前をキーにして複数の ComponentMgr を保有します。
  • ComponentMgr
    コンポーネント(ClientImpl、SupplierImpl など)の生成、管理を行うクラスの基底クラスです。
  • SingletonComponentMgr
    ComponentMgr のサブクラスで、コンポーネントを Singleton として管理します。テンプレートを使うともっとスマートに記述できますが、組込み分野での利用も想定して、今回はそこまでやっていません。
  • TraceInterceptor
    メソッドのトレースを行うアスペクトを定義したクラスです。Seasar2 だとコンポーネントとして DI コンテナに登録しますが、今回のプロトタイプではメソッドの中で生成・削除を行う設計になっています。

使用方法

example/MyDicon.h をご覧ください。Seasar2 などの DI コンテナでは、設定を XML ファイルで書きますが、今回のプロトタイプではコードで記述しています。

  1. メソッドにアスペクトを織り込みたいときは、コンポーネントのサブクラスを作成し、メソッドをオーバライドして、コンポーネントのメソッド呼び出し前後に Interceptor のコードを記述します。
    # これ、Proxy って名前にしちゃったけど、Proxy パターンじゃないですね。(^^;)
  2. コンポーネントごとに、ComponentMgr のサブクラスを作成し、コンポーネントの生成、削除のロジックを記述します。その際、必要に応じて別のコンポーネントや属性値を注入(Injection)します。
  3. 作成した ComponentMgr をコンテナに追加します。
  4. Main では、この DI コンテナを初期化し、コンテナからコンポーネントを取得して使用します。

ざっとこんな手順でしょうか。

C++ 版のポイント、制約など

  • http://d.hatena.ne.jp/makotan/20050925 の案だと、コンポーネントに DIObject という抽象クラスを作るとなっていますが、コンポーネントフレームワークに依存しないところが DI の良いところだと思うので、その制約は外しました。
  • DI 本体は何とかなりそうだが、Seasar2 風の AOP はリフレクションが無いとかなりつらい。一度やりかけて見たものの、実行時にメソッドの引数の情報がとれないので、あきらめて今回の設計にしました。でも、むしろこちらの方が C++ らしくてよい気がします。
  • 設定ファイルにこだわるなら、実行時に読み込んで設定を変更する形ではなく、やはりコード生成になりそうな気がします。プリプロセッサマクロで何とかなるレベルでもなさそう。

今後の展開

興味本位で作ってみたものの、最近仕事では全然 C++ 使っていないため、機能の整備・拡張とかをやるまでの余裕、気力はありません。(^^;)
興味を持たれた方がいらっしゃいましたら後はお任せします。