本文へスキップ
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

サンプル

DIは「抽象」「本番用の実装」「使う側」の3者に分かれます。実務どおりファイルも分けました(上のタブで切り替えられます)。

1. 抽象(IMessageSender.cs)。差し替えの継ぎ目です。

public interface IMessageSender
{
    void Send(string message);
}

2. 実装(ConsoleSender.cs)。本番用。SMS版を足したければ、このファイルの隣にSmsSender.csを作るだけです。

public class ConsoleSender : IMessageSender
{
    public void Send(string message) => Console.WriteLine($"[送信] {message}");
}

3. 使う側(OrderService.cs)。newが1つも無いことに注目してください。相手が誰かを知らないので、どんな実装にも差し替えられます。

public class OrderService
{
    private readonly IMessageSender _sender;      // 抽象に依存
    public OrderService(IMessageSender sender) => _sender = sender;   // 注入

    public void Complete(string id) => _sender.Send($"注文 {id} が完了しました");
}

4. 組み立てる側(Program.cs)。「どの実装を使うか」を決めるのはここだけ。実務ではDIコンテナがこの役目を担います。

// 依存を「外から」コンストラクタで受け取る
var service = new OrderService(new ConsoleSender());
service.Complete("A-001");   // [送信] 注文 A-001 が完了しました
  • クラスは具体クラスでなく**インターフェース(抽象)**に依存する(DIP/SOLIDのD)
  • 依存は new せず、コンストラクタの引数で外から渡す
  • テストでは IMessageSender のモックを渡せば、送信せずに検証できる
  • new が Program.cs(=組み立て役)に集まるのがDIの見た目の特徴

やってみよう

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

演習

new App(new JapaneseGreeter()).Run();   // こんにちは

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

// TODO: IGreeter を受け取り、Run() で greeter.Greet() を出力する App を定義してください
___
  • 期待される出力: こんにちは
ヒント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 パターンです。

実際に動かしてみよう

このレッスンのサンプルは、実務と同じように役割ごとの .cs ファイルへ分けてあります。下のエディタは最初からその複数ファイルが入った状態で、上のタブでファイルを切り替えられます。そのまま「▶ 実行」を押せば全ファイルをまとめてコンパイルして動かせるので、クラスを足したりファイルをまたいで書き換えたりしながら進めましょう(演習は本文のコードをコピーして Program.cs のタブに貼り付ければ試せます)。

C# — ブラウザ内で実行

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

次のレッスンへ →