はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【C#,Unity】DIコンテナを使う前にPoor man’s dependency injectionを検討してみてはどうでしょうか

はじめに

UnityでDI ContainerといったらZenjectExtenjectVContainerあたりが有名ですよね。

Dependency Injectionパターンを利用する場合は誰がインスタンスを生成・注入するかが難しい問題になりますが、その解決法の一つとしてDI Containerが利用されます。

ただDependency Injection = DI containerと思われている方もいるようですが、それは正しくありません。

今回はDIコンテナ以外の解決法の一つである、Poor man's dependency injectionについて紹介したいと思います。

下準備

以下のようなクラスを考えてみます。

public class RestaurantService
{
    private readonly IGeolocationService _geolocationService;
    
    public RestaurantService(IGeolocationService geolocationService)
    {
        _geolocationService = geolocationService;
    }

    public string GetRestaurant()
    {
        var location = _geolocationService.GetCurrentLocation();
        
        // 本来は座標からレストランを色々検索して返す
        return "レストラン: " + location;
    }
}

public interface IGeolocationService
{
    float GetCurrentLocation();
}

public class GeolocationService : IGeolocationService
{
    public float GetCurrentLocation()
    {
        // 本来はWebAPIを使ったり色々な処理をして座標を返す
        return 0;
    }
}
f:id:hanaaaaaachiru:20210626153251p:plain
クラス図


こちらのサイトを参考にしながら書きましたが,メソッドのシグネチャは適当です。注目してもらいたいところはRestaurantServiceのコンストラクタにてインスタンスを注入する箇所ですね。
Service LocatorとDependency InjectionパターンとDI Container - nuits.jp blog

このように依存するインスタンス(オブジェクト)を外部から注入しているのがDependency Injectionパターンです。

Poor man's dependency injection

このクラス達に対してPoor man's dependency injectionを適応してみます。

public class RestaurantService
{
    private readonly IGeolocationService _geolocationService;
    
    // 引数なしのコンストラクタを定義する
    public RestaurantService() : this(new GeolocationService()){}
    
    // テストをする時はこっちのコンストラクタを利用する
    public RestaurantService(IGeolocationService geolocationService)
    {
        _geolocationService = geolocationService;
    }

    public string GetRestaurant()
    {
        var location = _geolocationService.GetCurrentLocation();
        
        // 本来は座標からレストランを色々検索して返す
        return "レストラン: " + location;
    }
}
f:id:hanaaaaaachiru:20210626160226p:plain
クラス図

Poor man’s dependency injectionでは引数なしのコンストラクタ(厳密には注入したいインスタンスを引数にとらないコンストラクタ)を用意して、そこで依存先のインスタンスを設定してしまいます。

依存先のクラスが基本は変わらない場合にとても有効だと思います。

Dependency Injectionを適応した理由の一つでもあるテスタビリティについてもバッチリなのですが、実践してみましょう。

単体テストを行う

public class RestaurantServiceTest
{
    [Test]
    public void RestaurantServiceTestSimplePasses()
    {
        var restaurantService = new RestaurantService(new GetlocationServiceMock());
        
        Assert.AreEqual("レストラン: 0", restaurantService.GetRestaurant());
    }
}


public class GetlocationServiceMock : IGeolocationService
{
    public float GetCurrentLocation()
    {
        // あらかじめ決めた値を毎回返す
        return 0;
    }
}

まあいつもと変わりませんね。モックを生成してコンストラクタから注入してあげればOKです。

さいごに

考え方も作り方もシンプルなので、かなり有用なパターンだと思います。

こちらを使うか使わないかを状況次第といったところでしょうか。

また以前VContaienrを始めて触ってみたのですが、こちらもすごく使いやすかったのでよかったらチェックしてみてください。
www.hanachiru-blog.com

ではまた。