本文へスキップ
BecomeCoder

C#オブジェクト指向コース · 第3章 SOLID原則 · レッスン25

依存性逆転の原則(D)

ブラウザで完結

導入

Dependency Inversion Principle——「具体ではなく抽象に依存せよ」。上位のロジックが、下位の具体的な実装(DBやメール送信など)に直接依存すると、差し替えやテストが難しくなります。間にインターフェースを挟むのが解決策です。

図解

flowchart TB
    subgraph Before["密結合"]
        A1["OrderService"] -->|直接 new| B1["EmailSender(具体)"]
    end
    subgraph After["疎結合"]
        A2["OrderService"] --> I["INotifier(抽象)"]
        I --> B2["EmailSender"]
        I --> B3["SmsSender(差し替え可)"]
    end
    Before -->|抽象に依存| After

サンプル

// ✅ OrderService は「具体」ではなく「抽象(INotifier)」に依存する
INotifier notifier = new EmailSender();      // 実装を外から注入(DI)
var service = new OrderService(notifier);
service.Complete();   // 注文完了 → メールで通知

// SmsSender に差し替えても OrderService は無変更
service = new OrderService(new SmsSender());
service.Complete();   // 注文完了 → SMSで通知

interface INotifier { void Notify(string message); }

class EmailSender : INotifier
{
    public void Notify(string message) => Console.WriteLine($"{message} → メールで通知");
}
class SmsSender : INotifier
{
    public void Notify(string message) => Console.WriteLine($"{message} → SMSで通知");
}

class OrderService
{
    private readonly INotifier _notifier;   // 抽象に依存
    public OrderService(INotifier notifier) => _notifier = notifier;   // 外から注入
    public void Complete() => _notifier.Notify("注文完了");
}
  • 上位ロジックは具体クラスでなく**インターフェース(抽象)**に依存する
  • 実装は外から渡す(依存性注入 / DI)ことで差し替え可能になる
  • テスト時はモック実装を注入できる(TDDの章で活用する)

演習

var repo = new InMemoryRepository();
var app = new UserService(repo);
app.Register("Taro");
Console.WriteLine(repo.LastSaved);   // Taro

// TODO: IRepository(Save(string))に依存する UserService を定義してください。
//       Register(name) で _repo.Save(name) を呼びます
interface IRepository { void Save(string name); }
class InMemoryRepository : IRepository
{
    public string LastSaved = "";
    public void Save(string name) => LastSaved = name;
}
___
  • 期待される出力: Taro
ヒント1を見る

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

ヒント2を見る

class UserService { private readonly IRepository _repo; public UserService(IRepository repo) => _repo = repo; public void Register(string name) => _repo.Save(name); }

まとめ

  • 依存性逆転の原則:具体ではなく抽象に依存する
  • 実装は外から注入(DI)し、差し替え可能にする
  • テスト容易性・拡張性が大きく向上する(次章以降の設計の土台)

次章: 型と処理を抽象化する「ジェネリクスとデリゲート」へ進みます。

実際に動かしてみよう

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

C# — ブラウザ内で実行

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