開発現場では、「開発中は画面(コンソール)に出したいけれど、本番環境ではテキストファイルに残したい」ということがよくあります。こんなときは「 依存性の注入」(Dependency Injection 略してDI)が使えます。
実践例:ログ出力機能の切り替え
1. インターフェース(共通ルール)を作る
まず、「ログを出力する機能には、こういうメソッドが必要だよね」という規格を決めます。
C#
// ILogger.cs
public interface ILogger
{
// メッセージを受け取って記録する、というルールだけ決める
void Log(string message);
}2. 具体的な部品を作る
このルールに従って、2種類のロガー(記録係)を作ります。
A. 画面に表示するロガー(開発用)
C#
// ConsoleLogger.cs
using System;
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
// 黒い画面(コンソール)に表示する
Console.WriteLine($"[画面表示]: {message}");
}
}
B. ファイルに書き込むロガー(本番用)
C#
// FileLogger.cs
using System;
using System.IO;
public class FileLogger : ILogger
{
private string _filePath = "app_log.txt"; // 保存するファイル名
public void Log(string message)
{
// テキストファイルに追記する
// (実際の開発ではtry-catch等が必要ですが、簡略化しています)
string logText = $"{DateTime.Now}: {message}\n";
File.AppendAllText(_filePath, logText);
Console.WriteLine("(ファイルに書き込みました)");
}
}3. ロガーを使う業務クラス(注文処理)
ここがDIの核心です。
「注文処理」クラスは、「ログを取りたい」と思っていますが、「どうやって記録するか(画面かファイルか)」には関心を持ちません。
C#
// OrderService.cs
public class OrderService
{
// 具体的なクラス(FileLoggerなど)ではなく、インターフェースを持つ
private readonly ILogger _logger;
// 【依存性の注入ポイント】
// コンストラクタで「ILogger」を受け取るのがミソです。
// これにより、FileLoggerでもConsoleLoggerでも、どっちでも受け入れられます。
public OrderService(ILogger logger)
{
_logger = logger;
}
// 注文を確定するメソッド
public void PlaceOrder(string productName)
{
// 処理の開始をログに残す
_logger.Log($"{productName} の注文処理を開始します。");
// ... ここにデータベース保存などの複雑な処理があるとする ...
Console.WriteLine($" >> {productName} の注文が完了しました。");
// 処理の終了をログに残す
_logger.Log($"{productName} の注文処理が正常終了しました。");
}
}
4. 実際に動かしてみる(Mainメソッド)
プログラムの起動部分(Main)で、どの部品を使うか組み立てます。
C#
// Program.cs
class Program
{
static void Main(string[] args)
{
Console.WriteLine("== パターン1:開発中(画面に出したい) ==");
// 1. 画面用の部品を作る
ILogger devLogger = new ConsoleLogger();
// 2. 注入して注文サービスを作る
OrderService serviceDev = new OrderService(devLogger);
// 3. 実行
serviceDev.PlaceOrder("ゲーミングPC");
Console.WriteLine("\n--------------------------------------\n");
Console.WriteLine("== パターン2:本番運用(ファイルに残したい) ==");
// 1. ファイル用の部品を作る
ILogger productionLogger = new FileLogger();
// 2. 注入して注文サービスを作る(OrderServiceのコードは一切変更なし!)
OrderService serviceProd = new OrderService(productionLogger);
// 3. 実行
serviceProd.PlaceOrder("4Kモニター");
}
}
解説:ここが「実用的」なポイント
この設計の素晴らしい点は、OrderService(注文処理のロジック)を一切書き換えずに、ログの保存先を変更できたことです。
もしDIを使わず、OrderServiceの中で new FileLogger() と書いてしまっていたら……
- 「テストしたいから、ファイル書き込みじゃなくて画面に出して」と言われたとき、
OrderServiceのコードを書き換える必要があります。 - 書き換えるときに、うっかり注文処理のロジックを壊してしまうリスクがあります(バグの温床)。
DIを使えば、部品(ロガー)を外からパカッと入れ替えるだけなので、メインの処理を安全に守ったまま、周辺機能だけを柔軟に変更できるのです。

最近のコメント