これから始めるSpring.NET その1

なんかいまいちSpring.NETの認知度というか使用率が低いみたいなので、ここらで一度Spring.NETを知ってもらうために入門的なエントリでも書いてみることにする。
普段当たり前のように使っているので、今一度何故使うのかという事を考えてみたいというのもある。

Spring.NETって?

Spring.NETはIoC (Inversion of Control)*1と呼ばれる設計パターンを.NETで実現するためのフレームワークです。
最近ではDI (Dependency Injection)*2 コンテナと呼ばれていたりもします。

何ができるの?

オブジェクトのインスタンス化やSingleton、Prototypeインスタンスの切り替え、オブジェクト同士の関連付けなどをXMLベースの設定ファイルで定義し、使う側はDIコンテナに対してオブジェクトを要求するだけで必要な処理を全て施したオブジェクトを取得する事ができます。
簡単に言えばファクトリクラスを作らなくてもよくなるという事です。

何故使うの?

オブジェクト指向プログラミングを始めてからある程度経つと、クラスの責務の適切な分担や節度を持ったクラス継承構造の設計などができるようになってきます*3。そうなってくるとクラス間の依存関係をいかにして切り離すかという事に執心するようになるでしょう*4

通常、クラス間の依存関係を切り離すにはインターフェースを用います。
以下の例ではHogeクラスがExampleクラスをフィールドとして参照する際、具象クラスのExampleではなくインターフェースのIExampleをフィールドの型として使用しています。

例1
class Hoge {
    // インターフェースを参照する
    private IExample obj;
}
interface IExample {
}
class Example : IExample {
}

こうすることで、HogeクラスはExampleクラスの事を知ることなくExampleクラスの機能を(IExampleインターフェース経由で)利用することができます。これがクラス間の依存関係を切り離すということです。

しかし、このように依存関係を切り離しても以下のようにコンストラクタでExampleクラスをインスタンス化していては意味がありません。

例2
class Hoge {
    // インターフェースを参照する
    private IExample obj;
    
    public Hoge() {
        obj = new Example();
    }
}

これではHogeクラスは、

  • IExampleインターフェースを実装するクラスがExampleクラスである事
  • Exampleクラスのインスタンス化の方法(どのような引数を与えるかなど)

という事を知っている必要があるため、かなり密な依存関係を持っている事になります。

これは以下のようにIExampleインターフェースを実装するクラスのインスタンスをプロパティで渡すようにすれば解決できます

例3
class Hoge {
    private IExample obj;
    
    public IExample Example {
        get { return obj; }
        set { this.obj = value; }
    }
    public Hoge() {
    }
}

他にもコンストラクタの引数として渡す手もあります。

こうすればクラス間の依存関係は完全に切り離されたように見えるでしょう。

しかし待ってください、このHogeクラスをインスタンス化し、そのExampleプロパティに割り当てるExampleオブジェクトをインスタンス化し、そこに割り当てるという仕事はいったい誰(クラス)がやるのでしょうか?

それには以下のようにExampleクラスをインスタンス化するExampleFactoryクラスを用意することになります。そう結局誰かがExampleクラスをインスタンス化して、依存関係を持たなくてはならないのです。

例4
class Hoge {
    // インターフェースを参照する
    private IExample obj;
    
    public IExample Example {
        get { return obj; }
        set { this.obj = value; }
    }
    public Hoge() {
    }
}
class static ExampleFactory {
    public static IExample CreateInstance() {
        return new Example();
    }
}
class static Program {
    Hoge hoge = new Hoge();
    hoge.Example = ExampleFactory.CreateInstance();
}

Spring.NETはこのジレンマを解決して、クラス間の依存関係をコードから切り離してくれます。

先程のコードをSpring.NETで書き直すと以下のようになります。

例4
class Hoge {
    // インターフェースを参照する
    private IExample obj;
    
    public IExample Example {
        get { return obj; }
        set { this.obj = value; }
    }
    public Hoge() {
    }
}
class static Program {
    IApplicationContext context = new XmlApplicationContext("objects.config");
    // DIコンテナからオブジェクトを取得
    Hoge hoge = (Hoge)context.GetObject("hoge");
}

Spring.Context.Support.XmlApplicationContextクラスをオブジェクト定義ファイルのロケーションを引数に指定して、インスタンス化し、GetObjectメソッドでHogeクラスのインスタンスを取得しています。

オブジェクト定義ファイルとはクラス間の依存関係を定義するXMLベースの設定ファイルです。ここでは以下のように記述しています。

objects.config
<?xml version="1.0" encoding="utf-8"?>
<objects xmlns="http://www.springframework.net">
    <object name="hoge" type="Sample.Hoge, Sample">
        <property name="Example" ref="example" />
    </object>
    
    <object name="example" type="Sample.Example, Sample" />
</objects>

Spring.NETはこの定義ファイルを元にして、オブジェクトのインスタンス化、関連付けを行います。

これがSpring.NETです。

*1:制御の反転という意味、ハリウッドの法則などと呼ばれる事もある

*2:依存性注入という意味

*3:ならない人もいますけどね

*4:あくまでも、私はですが (;^_^