コンポーネントの自動登録機能で考えること

以前の日記で書いたAssemblyComponentAutoRegisterだが、一通り実装してみた。最初はAutoRegister周りだけを用意して、DIコンテナにぼこぼこと登録してやればよいと、簡単に考えていたのだが、これが甘かった。実際には

  • アセンブラとデプロイヤの構造の見直し
  • アノテーション(.NETの場合はカスタム属性)によるインジェクション機能の実装
  • 自動登録に必要な設定ファイル語彙の実装

これらが必要になったため、実際にはかなりの時間がかかった。特に、コンポーネントの自動登録を行うということは、アノテーション(カスタム属性)によるインジェクションが殆ど必須になるのだということに、最初は気がつかなかったのも大きい。
自動登録を行うということは、設定ファイルには何も明示的に記述されていない状況を指す訳で、その状態でどこかに格納されている型(Javaならファイルシステムやjar、.NETならアセンブリ)のインスタンスを生成する際に、インジェクションの詳細をどこに記述するか、ということからメタデータを利用するのは必然だったのだろう。
メタデータの取得先として、Seasa2つまりJavaでは使用するJSDK間の互換性を取る意味もあり、静的な定数を利用する、Backport175を利用する、Tigerで採り入れられたAnnotationを利用する等が用意されているが、.NETの場合は元々属性(Attribute)というメカニズムが用意されているわけで、これだけを使うことが自然である。(.NETは他のいろいろな用途に属性が使われており、開発者側から、メタデータを設定する手段として抵抗が無いなど、受け入れ易いメリットもある)
実際に自動登録機能を実装してみて、正直思ったのは、「便利だが扱いには相当気を使う」ということだ。設定ファイルという使い慣れた道具が無いということもだが、元々自動バインディング機能だけでも、コンポーネント間の依存を考慮しながらDIコンテナへの登録を行わなければならない、という複雑さがあった訳だが、更に設定ファイル無しでコンポーネントを登録する訳で、必要なメタデータをどこかに記述するという複雑さが加わることになる。これはフレームワークの提供者から見ると大した複雑さでは無いかもしれないが、アプリケーションの開発者にとっては、「ジグソーパズルのピースをどうあわせるか」的な複雑さを生み出してしまうのではないだろうかと懸念する。
この点、Springフレームワークなどは積極的には自動化、無設定化を推進していないが(確か、Auto Wiring Modeのデフォルトはnoだったはず)、これも一つの考え方であり一理あると思った。
コンポーネントの自動登録には、メタデータ、つまりアノテーション(カスタム属性)によるインジェクションが必須だと書いたが、この方法で完全にインジェクションをコントロールしようと思ったら、型の継承及びメソッドやプロパティのオーバライドが必要になってしまうのも悩ましいところだ。チームで開発を行う場合、ベースになるクラスに勝手にメタデータを追加することはまず許されないだろう。
ようするに、設定ファイルを書くか、クラスを継承してメソッド又はプロパティをオーバライドするか、をプログラマが自身で判断して選択するのは難しいと思う。

設定ファイルによるインジェクション(オーバライドは不要だが設定ファイルが必要)

  foobar



カスタム属性によるインジェクション(設定ファイルは不要だがオーバライドが必要)
public class InheritedHoge : Hoge.HogeImpl
{
    [Binding(BindingType = BindingType.MUST, Value = "foobar")]
    public override IFoobar Foobar
    {
        get { return base.Foobar; }
        set { base.Foobar= value; }
    }
〜
}
public class Foobar.FoobarImpl
{
〜
}
さて、あなたはどちらが好き?

なお、特殊なケースだが、設定ファイルが要らないのは勿論のこと、型をオーバライドしなくても自動登録だけで全ての依存関係を解決できる場合もあるだろう。実際にそういう例もあることはある。しかし、技術サンプルは別にして、一般的なアプリケーションにおいて使用するクラスの依存関係が、何も考慮せずに全て綺麗に解決する方が稀だろう。そもそも放って置いても1:1の関係になる場合を除いて、インジェクションの対象を選択しなくてはならないケースでは完全な自動登録は成立しないし。
自動化は便利だし、使えるのであれば上手く利用したい。では、便利さを享受しつつ、依存性を考慮してコンポーネントを登録する複雑さをできるだけ緩和してやるにはどうしたらよいだろう。結局は、他のフレームワークの基本同様に、「いかに使いやすいテンプレートを提供するか」というのが決め手になるのだろう。感覚として、自動登録と設定ファイルの記述を上手くあわせる、DIの対象を制限する(少なくとも.NET Frameworkが提供しているクラス、例えば System.Windows.Forms.Formなどの、プロパティがとてつもなく多いクラス等は、自動バインドの対象からは外したい)、等いろいろと考えはあるが、まだまとまらない。もう少し考えてから答えを出そうと思う。
あと、同じDIコンテナを使うのであっても、Webアプリケーションのようにサーバのメモリ上に必要なコンポーネントを全て乗せるのであればまだ良いが、クライアントのフロントエンドは初期化の時間、頻繁に再起動されることを考慮すると、できるだけ不要なコンポーネントはメモリ上に乗せたくない、というのもある。