首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 抽象工廠模式實作演進:從 Switch 到反射再到 DI 容器

     編輯:關於C#

前言

抽象工廠模式提供「建立一系列相關物件的介面,無需指定具體類別」。本文以水果工廠為例,展示四種實作演進:硬編碼 Switch、參數化工廠、反射動態建立、現代 DI 容器整合,並比較優缺點。

領域模型


public interface IFruit { string Name { get; } }
public class Apple : IFruit { public string Name => "蘋果"; }
public class Orange : IFruit { public string Name => "橘子"; }
public class Pear : IFruit { public string Name => "梨子"; }

階段一:硬編碼 Switch 工廠 (違反 OCP)


public class FruitFactoryV1
{
    public IFruit CreateFruit(string name) => name switch
    {
        "Apple" => new Apple(),
        "Orange" => new Orange(),
        _ => throw new ArgumentException($"Unknown fruit: {name}")
    };
}

缺點:新增水果需修改工廠類別,違反開放封閉原則。

階段二:參數化工廠方法 (仍需修改工廠)


public class FruitFactoryV2
{
    public IFruit CreateFruit(string name) => name switch
    {
        "Apple" => new Apple(),
        "Orange" => new Orange(),
        "Pear" => new Pear(), // 仍需修改此處
        _ => null
    };
}

客戶端程式碼簡化,但工廠仍需修改。

階段三:反射動態建立 (符合 OCP)


public class FruitFactoryV3
{
    private readonly Dictionary _fruitTypes = new();

    public FruitFactoryV3()
    {
        // 掃描組件中實作 IFruit 的非抽象類別
        foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
        {
            if (typeof(IFruit).IsAssignableFrom(type) && !type.IsAbstract && !type.IsInterface)
            {
                _fruitTypes[type.Name] = type; // 命名慣例:類別名 = 水果名
            }
        }
    }

    public IFruit CreateFruit(string name)
    {
        if (_fruitTypes.TryGetValue(name, out var type))
        {
            return (IFruit)Activator.CreateInstance(type);
        }
        throw new ArgumentException($"Unknown fruit: {name}");
    }
}

優點:新增水果只需新增類別,無需修改工廠。缺點:依賴命名慣例、效能稍低、無法處理建構函式參數。

階段四:泛型註冊工廠 (型別安全、支援依賴注入)


public class FruitFactoryV4
{
    private readonly Dictionary> _creators = new();

    public void Register(string name) where T : IFruit, new()
    {
        _creators[name] = () => new T();
    }

    public void Register(string name, Func factory)
    {
        _creators[name] = factory;
    }

    public IFruit CreateFruit(string name)
    {
        if (_creators.TryGetValue(name, out var creator))
            return creator();
        throw new ArgumentException($"Unknown fruit: {name}");
    }
}

// 使用範例
var factory = new FruitFactoryV4();
factory.Register("Apple");
factory.Register("Orange");
// 甚至支援複雜建構邏輯
factory.Register("SpecialPear", () => new Pear { /* 初始化 */ });

階段五:整合 DI 容器 (Microsoft.Extensions.DependencyInjection)


using Microsoft.Extensions.DependencyInjection;

public interface IFruitFactory
{
    IFruit Create(string name);
}

public class DiFruitFactory : IFruitFactory
{
    private readonly IServiceProvider _sp;
    private readonly Dictionary _map = new();

    public DiFruitFactory(IServiceProvider sp)
    {
        _sp = sp;
        // 掃描已註冊的服務
        foreach (var svc in sp.GetServices())
        {
            _map[svc.GetType().Name] = svc.GetType();
        }
    }

    public IFruit Create(string name)
    {
        if (_map.TryGetValue(name, out var type))
            return (IFruit)_sp.GetRequiredService(type);
        throw new ArgumentException($"Unknown fruit: {name}");
    }
}

// 註冊
var services = new ServiceCollection();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();

var provider = services.BuildServiceProvider();
var factory = provider.GetRequiredService();
var apple = factory.Create("Apple");

比較總結表

版本符合 OCP型別安全支援建構參數效能適用場景
V1 Switch❌✅✅最高極簡單、固定類別
V2 參數化❌✅✅高客戶端簡化,但仍需修改工廠
V3 反射✅❌ (執行時)❌ (無參建構)中 (首次較慢)插件系統、動態擴充
V4 委派註冊✅✅✅高框架開發、需靈活建構邏輯
V5 DI 容器✅✅✅ (完整 DI)高 (容器優化)企業級應用、微服務、現代 .NET

最佳實務建議

  1. 簡單專案:V4 委派註冊工廠,輕量且型別安全
  2. 框架/函式庫:提供 IFruitFactory 介面,讓使用者自行註冊
  3. 企業應用:直接使用 DI 容器內建功能,配合 IServiceProvider 或 IServiceScopeFactory
  4. 避免:在業務邏輯中直接 new 具體類別,統一透過工廠抽象

總結

抽象工廠模式核心在於「將物件建立邏輯封裝並抽象化」。從硬編碼演進到反射、委派註冊、DI 容器,本質都是延遲綁定具體類別。現代 .NET 開發首選 DI 容器整合,既符合 SOLID 原則,又獲得生命週期管理、攔截、AOP 等額外紅利。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved