モダンC#で進化するFactory Method ― ジェネリクスとDIによる設計最適化 Part3

このPart 3では、前回の「理解」から一歩進み、
モダンC#構文(C#10〜12)を使ってFactoryをより型安全・シンプル・柔軟に最適化する方法を解説します。

目次

はじめに:昔のFactoryと今のFactoryの違い

Factory Methodパターンは1990年代に生まれた設計手法ですが、
C#の進化によって“より短く、より安全に”書ける時代になりました。

古いコードでは、こうしたFactoryがよくありました。

public abstract class Creator
{
    public abstract IReport CreateReport();
}

ここで毎回 IReport 型を返すため、
キャストや冗長なクラス宣言が増えがちです。

しかし、C# 10以降では ジェネリクス・式ボディ・new()簡略構文・switch式 などにより、
Factoryのコードを半分以下にできます。


ジェネリクスで型安全なCreatorを実現する

ジェネリクスを導入すると、Creatorが生成する型を明確に制約できます。

// Creator<T>:型安全な抽象Creator
public abstract class Creator<T> where T : IReport
{
    public abstract T CreateReport();

    public void Execute()
    {
        var report = CreateReport();
        Console.WriteLine($"[{report.GetType().Name}] を生成しました。");
        report.Export();
    }
}

where T : IReport により、
Creatorが扱えるのは「IReportを実装する型」に限定されます。
→ キャスト不要、型安全、IDE補完も効く。

具体的Creator(ジェネリクス対応)

public class PdfCreator : Creator<PdfReport>
{
    public override PdfReport CreateReport() => new();
}

public class ExcelCreator : Creator<ExcelReport>
{
    public override ExcelReport CreateReport() => new();
}

new() の簡略構文を使えば、生成もワンライン。
C# 10 以降では、明示的な型記述すら不要です。


実行例

class Program
{
    static void Main()
    {
        var pdfCreator = new PdfCreator();
        pdfCreator.Execute();

        var excelCreator = new ExcelCreator();
        excelCreator.Execute();
    }
}

出力結果:

[PdfReport] を生成しました。
PDFレポートを出力しました。
[ExcelReport] を生成しました。
Excelレポートを出力しました。

モダン構文でFactoryをシンプルにする

モダンC#の特徴を取り入れると、Factoryはより洗練されます。

機能使用例効果
式ボディメソッドpublic override PdfReport CreateReport() => new();可読性・短縮性UP
new() 簡略構文new();型名省略で安全な省略
switch式key switch { "PDF" => new PdfCreator(), ... }条件分岐の明示化
record型record ReportConfig(string Type, string Path);Factory登録情報を簡潔に表現

パターンマッチングによるCreator選択

Factoryを動的に選ぶ際にも、
C#の switch 式を使うと柔軟で読みやすくなります。

static Creator<IReport> SelectCreator(string key) => key switch
{
    "PDF" => new PdfCreator(),
    "Excel" => new ExcelCreator(),
    _ => throw new ArgumentException("不明なレポート形式")
};

旧来のif/elseに比べて、
構造的に安全でミスが起きにくいコードになります。

DI(依存性注入)とFactoryの統合

モダンC#でFactoryを使う最大の強みは、DIコンテナとの統合です。
ASP.NET Coreでは IServiceCollection に登録するだけで、
Creatorを実行時に差し替え可能になります。

services.AddTransient<Creator<PdfReport>, PdfCreator>();
services.AddTransient<Creator<ExcelReport>, ExcelCreator>();

利用側は依存を抽象型で受け取るだけ:

public class ReportService
{
    private readonly Creator<IReport> _creator;

    public ReportService(Creator<IReport> creator)
    {
        _creator = creator;
    }

    public void Run() => _creator.Execute();
}

これにより、アプリ全体の構成変更を「設定」だけで完結できるようになります。

Factory Registryで柔軟な拡張構造を実現する

「製品が増えすぎてCreatorクラスが多くなった」場合は、
Factoryをレジストリ形式(辞書管理)に変えると柔軟性が上がります。

public static class ReportFactoryRegistry
{
    private static readonly Dictionary<string, Func<IReport>> _registry = new()
    {
        { "PDF", () => new PdfReport() },
        { "Excel", () => new ExcelReport() }
    };

    public static IReport Create(string key)
    {
        if (_registry.TryGetValue(key, out var factory))
            return factory();
        throw new ArgumentException($"不明な形式: {key}");
    }
}

利用例

IReport report = ReportFactoryRegistry.Create("PDF");
report.Export();

Creatorクラスを持たないこの構造は、
“Simple Factory”と“Factory Method”の中間形で、
小規模アプリではとても有効です。


実務応用:AI・Web・ゲームでも使われるFactory Method

Factory Methodは「生成責務を分離する」という発想ゆえ、
多様な領域で活躍しています。

分野具体例Factoryの役割
AI/MLアプリGPT・Geminiなどモデル切替モデル生成をFactoryで統一
Web APIクライアント認証(Basic / OAuth)認証タイプごとにCreator切替
ゲーム開発Unityでのエフェクト・敵生成Prefab生成をFactory化して一元管理

特に、依存の切替やプラグイン拡張が必要な環境では、
Factory Methodが保守コストを劇的に下げます。


まとめ:Factory Methodは今も“現役の設計”

Factory Methodは古い概念に見えて、
現代のC#アプリケーション設計でも中核的存在です。

✅ 今回の要点

  • ジェネリクスで型安全なCreatorを実現
  • new() 簡略構文・switch式で冗長性を削減
  • DIと統合して拡張性を最大化
  • Factory Registryで軽量な柔軟拡張も可能

結論:
Factory Methodは「拡張を前提とした設計」を支える最もシンプルなパターン。
それを現代C#の文法で磨くことで、
短く・安全で・保守に強いコードが生まれる。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次