依存性注入パターンの実装

DIパターン

依存性注入を実装する主要なパターンとして、Composition Root、Constructor Injection、Method Injection、Property Injectionの4つが存在する。各パターンは特定のコンテキストで効果的に機能する。

Composition Root

オブジェクトグラフの構築をアプリケーションエントリポイント近くに集中させる設計手法。複数モジュールから構成されるシステムでは、オブジェクトの組み立てを単一の論理的な場所で行う。

// エントリポイントでのオブジェクト構築例
public class Startup {
    public void ConfigureServices(IServiceCollection services) {
        services.AddSingleton<IDataService>(provider => 
            new DataService(
                new SqlRepository(
                    new DbContext(Configuration.GetConnectionString("Default")))
        );
    }
}

特徴:

  • DIコンテナの使用はこの領域に限定
  • アプリケーションコアから構成詳細を分離
  • モジュール間の結合度を低減

Constructor Injection

クラスが依存するコンポーネントをコンストラクタパラメータで明示的に要求する手法。

public class OrderProcessor {
    private readonly IPaymentGateway _paymentGateway;
    private readonly IInventoryService _inventoryService;

    public OrderProcessor(IPaymentGateway paymentGateway, IInventoryService inventoryService) {
        _paymentGateway = paymentGateway ?? throw new ArgumentNullException(nameof(paymentGateway));
        _inventoryService = inventoryService ?? throw new ArgumentNullException(nameof(inventoryService));
    }
    
    public void Process(Order order) {
        _paymentGateway.Charge(order.Total);
        _inventoryService.UpdateStock(order.Items);
    }
}

ベストプラクティス:

  • 依存関係はreadonlyフィールドで保持
  • 単一の公開コンストラクタを使用
  • コンストラクタ内でビジネスロジックを実行しない

Method Injection

操作ごとに依存関係が変化する場合、メソッドパラメータとして依存オブジェクトを渡す手法。

public class CurrencyConverter {
    public Money Convert(Money amount, IExchangeRateProvider provider) {
        if (provider == null) throw new ArgumentNullException(nameof(provider));
        return new Money(amount.Value * provider.GetRate(amount.Currency), targetCurrency);
    }
}

// 使用例
var converter = new CurrencyConverter();
var yenAmount = converter.Convert(dollars, new BankExchangeProvider());

適用ケース:

  • プラグインアーキテクチャ
  • ドメインエンティティの振る舞い拡張
  • 操作コンテキストの動的提供

Property Injection

オプショナルな依存関係をプロパティ経由で注入する手法。ローカルデフォルト実装がある場合に有効。

public class CachingService {
    public ICacheProvider CacheProvider { get; set; } = new MemoryCacheProvider();
    
    public Data GetData(string key) {
        return CacheProvider.Get(key) ?? FetchFromSource(key);
    }
}

// デフォルト実装の上書き
var service = new CachingService();
service.CacheProvider = new DistributedCacheProvider();

注意点:

  • コンストラクタインジェクションが優先される
  • プロパティ変更のタイミング問題に注意
  • ライブラリ開発での拡張ポイントに適する

パターン選択ガイド

状況 推奨パターン
必須依存関係 Constructor Injection
操作ごとの依存関係 Method Injection
オプショナル拡張 Property Injection
オブジェクト構成 Composition Root

Null Objectパターン

依存関係のデフォルト実装として振る舞いのない実装を提供する手法。条件分岐を削減しコードを簡潔にする。

public class NullLogger : ILogger {
    public void Log(string message) { /* 何もしない */ }
}

// 使用例
var processor = new DataProcessor(new NullLogger());

タグ: 依存性注入 デザインパターン csharp オブジェクト指向設計

6月16日 16:05 投稿