本文へスキップ
BecomeCoder

C#デザインパターンコース · 第3章 現代の設計パターン · レッスン19

依存性注入(DI)とコンストラクタインジェクション

ブラウザで完結

導入

クラスの中で new EmailSender() と書くと、その相手にがっちり固定されてしまい、テスト時に差し替えられません。**依存性注入(DI)**は「必要な相手を自分で作らず、外から受け取る」設計です。これが ASP.NET Core などのDIコンテナの土台になります。

図解

flowchart LR
    subgraph before["直接 new(密結合)"]
        A1["Notifier"] -->|new| B1["EmailSender 固定"]
    end
    subgraph after["注入(疎結合)"]
        A2["Notifier"] -->|コンストラクタで受け取る| I["INotifier 抽象"]
        I -.-> B2["Email / SMS / モック"]
    end
    style after fill:#e8f5e9

サンプル

// 依存を「外から」コンストラクタで受け取る
var service = new OrderService(new ConsoleSender());
service.Complete("A-001");   // [送信] 注文 A-001 が完了しました

interface IMessageSender
{
    void Send(string message);
}
class ConsoleSender : IMessageSender
{
    public void Send(string message) => Console.WriteLine($"[送信] {message}");
}
class OrderService
{
    private readonly IMessageSender _sender;      // 抽象に依存
    public OrderService(IMessageSender sender) => _sender = sender;   // 注入

    public void Complete(string id) => _sender.Send($"注文 {id} が完了しました");
}
  • クラスは具体クラスでなく**インターフェース(抽象)**に依存する(DIP/SOLIDのD)
  • 依存は new せず、コンストラクタの引数で外から渡す
  • テストでは IMessageSender のモックを渡せば、送信せずに検証できる

やってみよう

ConsoleSender とは別に、送信内容を溜め込むだけの FakeSender : IMessageSender を作って渡してみましょう。OrderService を一切変えずに振る舞いを差し替えられます。

演習

interface IGreeter { string Greet(); }
class JapaneseGreeter : IGreeter { public string Greet() => "こんにちは"; }

// TODO: IGreeter を受け取り、Run() で greeter.Greet() を出力する App を定義してください
___

new App(new JapaneseGreeter()).Run();   // こんにちは
  • 期待される出力: こんにちは
ヒント1を見る

class App { private readonly IGreeter _g; public App(IGreeter g) => _g = g; public void Run() => Console.WriteLine(_g.Greet()); }

ヒント2を見る

依存 IGreeter をコンストラクタで受け取り、フィールドに保持します

まとめ

  • DIは「依存を自分で作らず外から受け取る」設計
  • 具体でなく抽象(インターフェース)に依存する
  • 差し替え・テストが容易になり、DIコンテナの土台になる

次回: データアクセスを抽象化する Repository パターンです。

実際に動かしてみよう

本文のサンプルや演習のコードは、コードブロック右上の「コピー」ボタンでコピーして、下のエディタに貼り付ければそのまま実行できます。

C# — ブラウザ内で実行

ブラウザ内でC#を動かす環境を読み込みます(初回のみ数秒)。
スクロールして表示された時点でも自動で読み込まれます。