shuhelohelo’s blog

Xamarin.Forms多めです.

DIとDIコンテナについて復習

DI:

DI(依存性の注入)は、あるクラス(Hogeクラス)の中で別のクラス(Fugaクラス)のインスタンスを利用する場合、FugaクラスをHogeクラス内でインスタンス化(new)するのではなく、コンストラクタ、プロパティ、メソッド経由でHogeクラスにインスタンスを渡すこと。

依存性というとわかりにくいけれど、「クラスHogeが利用する(依存する)オブジェクト」のこと。 それを内部で生成するのではなく外部から渡す(注入:injection)するので「依存性の注入:Dependency Injection」という。

コンストラクタ経由の注入(コンストラクタインジェクション)がもっとも良いといわれる。それは、Hogeクラスのインスタンス化時にHogeクラスが必要とするオブジェクトを全部コンストラクタに渡さなければならないので、プロパティ経由、メソッド経由のようにうっかり渡し忘れということがないし、Hogeを利用する(コーディングする)側としてHogeクラスがどのクラスを使っているのか、という依存関係を把握しやすいから。

DIパターンを使うことのメリット:

  • クラス間の結合を低くできること
  • それによってテスタビリティを高くできること

DIパターン(コンストラクタインジェクション)を使ったHogeクラスの実装は以下のようになる。

public class Hoge
{
    private readonly Fuga _fuga;
    public Hoge(Fuga fuga)
    {
        _fuga = fuga;
    }

    public void DoSomething()
    {
        _fuga.SayHello();
    }
}


//利用する側

static void Main()
{
    var hoge = new Hoge(new Fuga());

    hoge.SayHello();
}

このとき、FugaのインターフェースIFugaを作って、このインターフェース経由でDIするようにすると、テスタビリティが高くてなお良い。

public interface IFuga
{
    public void SayHello();
}

public class Fuga : IFuga
{
    public void SayHello()
    {
        //実装
    }
}


public class Hoge
{
    private readonly IFuga _fuga;
    public Hoge(IFuga fuga)
    {
        _fuga = fuga;
    }

    public void DoSomething()
    {
        _fuga.SayHello();
    }
}

これでIFugaを実装するクラスならなんでもOKになるので、テスト用のクラス(例:FugaMockクラス)を作って渡すようにしても、Hogeクラスの実装に変更を加える必要がない。

DIコンテナ:

DIパターンを使うと、依存するクラスが多くなるとDI地獄(?)的なことになる。 (その場合はクラスの設計が悪い可能性が高いけれども)

var hoge = new Hoge(new Fuga(), new Bar(), new Foo()......);

これはまだいい方で、Fugaクラスが他のクラス(Misoクラス)に依存していて、MisoクラスがDaizuクラスに依存している、などとなるとDIパターンを使うと以下のようになる。

var hoge = new Hoge(new Fuga(new Miso(new Daizu), new Bar(), new Foo().... );

もう意味がわからない。

このクラス生成時の依存関係を解決する仕組み(フレームワークだったりライブラリ)がDIコンテナ。

DIコンテナ(の機能を持つクラスだよ)にクラスを登録しておき、DIコンテナ経由でインスタンス化することで、注入するインスタンスの生成などを自動的にやってもらうことができる。

例:擬似コード的ななにか

var container = new Container();//ライブラリやフレームワークが提供するクラス

//まずはDIコンテナにクラスを登録する
container.Register<IFuga, Fuga>();//interface経由なら
container.Register<Miso>();//クラス直指定なら
container.Register<Daizu>();
container.Register<Bar>();
container.Register<Foo>();

//Hogeクラスのインスタンス化
var hoge = container.Resolve<Hoge>();//必要なインスタンスの準備はこの1行で全て解決!

素晴らしいですね。

C#のDIコンテナのUnityを使った例↓

shuhelohelo.hatenablog.com