Menu
Coddy logo textTech

依存性の注入 (DI) の基礎

CoddyのC#ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 46/70。

Dependency Injection(DI)とは、class 自身が依存関係を作成するのではなく、外部から受け取る技法です。これにより、コードの柔軟性とテスト容易性が向上し、疎結合になります。

DI を使わない場合、クラスは独自の依存関係を作成するため、変更やテストが困難になります。

public class OrderService
{
    private EmailSender sender = new EmailSender();  // 密結合
    
    public void PlaceOrder() => sender.Send("Order placed");
}

DIでは、依存関係はコンストラクターを通じて渡されます。

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

public class EmailSender : IMessageSender
{
    public void Send(string message) => Console.WriteLine("Email: " + message);
}

public class OrderService
{
    private IMessageSender sender;
    
    public OrderService(IMessageSender sender)
    {
        this.sender = sender;
    }
    
    public void PlaceOrder() => sender.Send("Order placed");
}

これで、OrderServiceはメール、SMS、テスト用のモック送信者のいずれを使用しているかを知る必要も、気にする必要もありません。オブジェクトが作成されるときに依存関係が「injected」されます。

IMessageSender emailSender = new EmailSender();
OrderService service = new OrderService(emailSender);
service.PlaceOrder();  // メール: 注文が完了しました

このパターンは、具象実装ではなく抽象化(インターフェース)に依存するという原則に従います。これにより、それを使用するclassを変更せずに実装を入れ替えられるため、単体テストの実施や柔軟なアーキテクチャの維持に不可欠です。

challenge icon

チャレンジ

簡単

依存性注入の力を示すロギングシステムを構築しましょう。ログの書き込み方法をハードコーディングする代わりに、具体的な仕組みを知らなくても、コンソール、ファイルのシミュレーション、その他あらゆるログ出力先で動作できる柔軟な Logger class を作成します。

コードを3つのファイルに分けて整理します。

  • ILogWriter.csLogging namespace に ILogWriter という名前の interface をDefineします。この interface は、あらゆるログ出力先に共通する契約を表し、メッセージがどこに書き込まれたかを説明する string を返す単一の method Write(string message) を持つ必要があります。
  • LogWriters.csLogging namespace に ILogWriter の2つの具体的な実装をCreateします。
    • ConsoleLogWriter - its Write method は Console: {message} を返します
    • FileLogWriter - constructor を通じて filename を受け取り、its Write method は File[{filename}]: {message} を返します
  • Logger.csLogging namespace に、constructor injection を通じて dependency を受け取る Logger class をCreateします。Logger は次のようにします。
    • constructor を通じて ILogWriter を受け取り、private field に格納する
    • 注入された writer に処理をdelegatesし、結果を返す Log(string message) method をhaveする
  • Program.cs:異なる2つの logger(1つは ConsoleLogWriter、もう1つは FileLogWriter)を作成して dependency injection を実演します。どの dependency が注入されるかによって、同じ Logger class の動作がどのように変わるかを示します。

2つの入力を受け取ります。

  • file logger 用の filename
  • ログに記録する message

ConsoleLogWriter を使用して Logger をCreateし、message をログに記録します。次に、FileLogWriter を使用して別の Logger をCreateし(入力の filename を使用)、同じ message をログに記録します。それぞれの結果を独自の行にPrintします。

たとえば、入力が app.logSystem started の場合、出力は次のようになります。

Console: System started
File[app.log]: System started

Logger class 自体は決して変更されないことに注目してください。あらゆる ILogWriter 実装で動作します。これが dependency injection の本質です。Logger は抽象(interface)に依存し、具体的な実装は外部から "injected" されます。後から Logger class をまったく変更せずに、DatabaseLogWriterCloudLogWriter を簡単にAddできます。

自分で試してみよう

using System;
using Logging;

class Program
{
    public static void Main(string[] args)
    {
        // 入力を読み取る
        string filename = Console.ReadLine();
        string message = Console.ReadLine();

        // TODO: ConsoleLogWriterを使用してLoggerを作成し、メッセージをログに記録する
        // 結果を出力する

        // TODO: FileLogWriterを使用して(filenameを使用)別のLoggerを作成し、同じメッセージをログに記録する
        // 結果を出力する
    }
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: C#オンラインコンパイラ