質問: new Subject(sender); subject.send(); ```
回答: コードはC#で悪いんだけども、コンストラクターインジェクションをベースに話をしよう。 まず、コンストラクターは情報さえあれば呼び出すことができる。 そこはわかってるかな? C#やJavaはリフレクションができるから特にある型のインスタンス化は簡単にできる。 C++とかでも関数ポインタを集積すればそこを経由してインスタンス生成を間接的に行うことができる。 とにかく...最初に大事なことは、`new 型名`ってしなくても、情報からインスタンス化することは可能だってことだ。 そういう情報を集積して、取得したいクラスからその必要なパラメーター(子供)にわたって連結して値を生成できるように再帰呼び出しする仕組みを作るんだ。それが大体DIコンテナのやってることだ。 下記は、今、このページで打っただけだから下記のコードはぐだぐだだけども、一通り必要そうなコードを書いてみた。 ``` // あるクラスのインジェクションに関係する情報を集める // これは関数で支持を出しても良いし、よりよい形ではリフレクションでも良い BindingInfo Bind() { // このコンストラクタにどんな情報があるかを事前にまとめておくためのクラスがあるとして var info = new BindingInfo(typeof(T)); // まずは器を用意しておく cache[typeof(type)] = info; return info; } // で、実装クラスの関連情報を作成した収集クラスに集める public class BindingInfo { // 例えばある型と紐づける public BindedTypeInfo To(Type type) { // 紐づける型が派生形であることを調べる this.Type.IsAssignableFrom(type); // C#だとこんな風にコンストラクタの一覧はとってこれるし var constructors = type.GetConstructors(); foreach (var constructor in constructors) { // ConstructorInfoからはGetParametersとかで引数はとってこれる this.Add(new BindingConstructorInfo(constructor)); // そもそも、ConstructorInfoは実行できる // constructor.Invoke(/*引数はオブジェクトの配列*/); } } // もしくはあるインスタンスと紐づけたり public BindedTypeInfo ToConstant(T value) { // ここでは、この型情報のインスタンスが欲しい時に // ここで受け取ったvalueを返すようにする // で、紐づけられた関連情報を更にreturn することによって // 細かいオプションを足せるようにしてもいいだろうね! } } // DIコンテナで値を取得するときは、再帰的に生成を行っていく public T Get() { // ある型の情報からインスタンスを取るために必要なものを考える var info = cache[typeof(T)]; // 例えばinfoがコンスタントならその値を返すだろうし if (info.IsConstant) { return info.ConstantValue; } else { // じゃなくてインスタンスを生成しなければならないなら // 必要なコンストラクタの引数を生成するために再帰的にGetする var constructor = info.GetConstructor(); var parameters = new List(); foreach(var parameterInfo in constructor.GetParameters()) { // Getメソッドを再帰的に呼び出せばいつか端にたどり着く parameters.Add(this.Get(parameterInfo.ParameterType)); } // で、生成したパラメータを使ってコンストラクタを実行する return constructor.Invoke(parameters); } } ``` 情報を収集し、展開する。 収集方法は別にメソッドでもいいし、一般的なDIコンテナみたいになんらかのファイルでもいい。 基本は、あるインスタンスの生成方法を集積して、再帰的に呼ぶだけ。 インスタンス化できなければ情報不足として例外を投げればいい。 実行時の動作変更用のオプションの拡張、インスタンス化に関する情報のキャッシュで高速化だとか、まあそういう細かいのはいっぱいあるだろうと思う。 とりあえず... 既存のOSSのDIコンテナのソースを見よう! 追記 == 上のクラスの使い方の想定をかいてなかったね。 ``` var container = new MyDIContainer(); // ISomeClassという型には、ImplemetedClassをインスタンス化してよこせ、と指示を出す。 container.Bind().To(); var implemented = container.Get(); ``` ちなみにこれはNinjectっていうC#のDIコンテナを僕が使っているのでその構造に強く影響を受けている。 NinjectはDIコンテナだけど設定ファイルを使わないので、その分プログラマーには直観的だと思う。 ymlだのXMLだので設定ファイルを書いてインジェクションするよりよっぽど使うのが簡単なので、発想はC#未経験でもページを見ればわかると思う。 設定ファイルを書くDIコンテナは、設定ファイルを読み込んで動作を変更できるようにちょっと変えただけだ。 設定ファイルを使う場合は、現在アプリケーションに読み込まれてる型情報をリフレクションで取得しておいて、その情報に設定ファイルから得たオプションを適用していく、という形になるだろうね。