You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于枚举参数创建实例?规避开闭原则违反的方案探讨

符合开闭原则的水果实例创建方案

你当前的简单工厂实现确实违反了开闭原则——每次新增水果类型都要修改工厂类的switch逻辑。下面提供几种更优的解决方案,都能做到新增水果类型时无需修改工厂核心代码:

方案一:特性+反射自动映射

通过自定义特性关联枚举与水果类,初始化时扫描所有水果类建立映射,后续新增水果只需给类添加特性即可。

步骤1:定义关联特性

[AttributeUsage(AttributeTargets.Class)]
public class FruitTypeAttribute : Attribute
{
    public Fruits FruitType { get; }

    public FruitTypeAttribute(Fruits fruitType)
    {
        FruitType = fruitType;
    }
}

步骤2:给水果类标记特性

[FruitType(Fruits.Apple)]
public class Apple : Fruit { }

[FruitType(Fruits.Orange)]
public class Orange : Fruit { }

[FruitType(Fruits.Banana)]
public class Banana : Fruit { }

步骤3:实现工厂类

public class FruitFactory
{
    private static readonly Dictionary<Fruits, Type> _fruitTypeMap;

    static FruitFactory()
    {
        // 程序启动时扫描所有继承Fruit的类,构建枚举与类型的映射表
        _fruitTypeMap = AppDomain.CurrentDomain.GetAssemblies()
            .SelectMany(asm => asm.GetTypes())
            .Where(type => typeof(Fruit).IsAssignableFrom(type) && !type.IsAbstract)
            .Select(type => new
            {
                Type = type,
                Attr = type.GetCustomAttribute<FruitTypeAttribute>()
            })
            .Where(x => x.Attr != null)
            .ToDictionary(x => x.Attr.FruitType, x => x.Type);
    }

    public Fruit CreateFruit(Fruits fruitType)
    {
        if (_fruitTypeMap.TryGetValue(fruitType, out var type))
        {
            return (Fruit)Activator.CreateInstance(type);
        }
        throw new ArgumentOutOfRangeException(nameof(fruitType), "不支持的水果类型");
    }
}

优势:新增水果仅需添加类和特性,无需修改工厂代码;缺点:反射存在轻微性能开销(但初始化仅执行一次)。

方案二:注册式工厂

通过提供注册方法,允许外部将枚举与对应的实例创建逻辑绑定,完全解耦工厂与具体水果类。

工厂实现

public class FruitFactory
{
    private readonly Dictionary<Fruits, Func<Fruit>> _creators = new();

    // 注册水果创建逻辑
    public void Register(Fruits fruitType, Func<Fruit> creator)
    {
        if (_creators.ContainsKey(fruitType))
        {
            throw new InvalidOperationException($"已注册{fruitType}的创建逻辑");
        }
        _creators[fruitType] = creator;
    }

    // 创建实例
    public Fruit CreateFruit(Fruits fruitType)
    {
        if (_creators.TryGetValue(fruitType, out var creator))
        {
            return creator();
        }
        throw new ArgumentOutOfRangeException(nameof(fruitType), "不支持的水果类型");
    }
}

使用方式

// 初始化工厂并注册所有水果
var factory = new FruitFactory();
factory.Register(Fruits.Apple, () => new Apple());
factory.Register(Fruits.Orange, () => new Orange());
factory.Register(Fruits.Banana, () => new Banana());

// 创建实例
var apple = factory.CreateFruit(Fruits.Apple);

优势:无反射开销,支持自定义创建逻辑(例如注入依赖);缺点:需要手动注册所有水果类型。

方案三:结合依赖注入容器

如果项目已使用DI容器(如Autofac、Microsoft.Extensions.DependencyInjection),可直接利用容器管理实例创建,同时维护枚举与服务类型的映射。

示例(Microsoft.Extensions.DependencyInjection)

// 注册水果服务
var services = new ServiceCollection();
services.AddTransient<Apple>();
services.AddTransient<Orange>();
services.AddTransient<Banana>();

// 建立枚举与服务类型的映射
var fruitServiceMap = new Dictionary<Fruits, Type>
{
    { Fruits.Apple, typeof(Apple) },
    { Fruits.Orange, typeof(Orange) },
    { Fruits.Banana, typeof(Banana) }
};

var provider = services.BuildServiceProvider();

// 创建实例的方法
public Fruit CreateFruit(Fruits fruitType)
{
    if (fruitServiceMap.TryGetValue(fruitType, out var serviceType))
    {
        return (Fruit)provider.GetService(serviceType);
    }
    throw new ArgumentOutOfRangeException(nameof(fruitType));
}

优势:复用DI容器的生命周期管理,支持依赖注入;缺点:依赖项目的DI配置。

内容的提问来源于stack exchange,提问作者Iliya Belan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 02:35:27