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

遵循开闭原则重构Invoice代码,如何免if判断实例化对应发票类?

实现方案

推荐以下两种符合开闭原则的实现方式:

方案1:轻量字典映射工厂(无第三方依赖)

通过字典预先建立发票类型字符串和对应实例创建逻辑的映射,运行时直接通过key匹配对应实现,无需编写分支判断:

// 发票工厂类
public static class InvoiceFactory
{
    // 不区分大小写的映射字典,Value为实例创建委托
    private static readonly Dictionary<string, Func<IInvoice>> _invoiceCreatorMap = new(StringComparer.OrdinalIgnoreCase)
    {
        { "invoice", () => new Invoice() },
        { "discountinvoice", () => new DiscountInvoice() }
    };

    public static IInvoice Create(string invoiceType)
    {
        if (_invoiceCreatorMap.TryGetValue(invoiceType, out var creator))
        {
            return creator();
        }
        throw new NotSupportedException($"未适配的发票类型:{invoiceType}");
    }
}

调用方式:

static void Main(string[] args)
{
    // 模拟从API获取的模型
    InvoiceModel invoiceModel = GetInvoiceFromApi();
    // 直接通过工厂获取对应实例,无分支判断
    IInvoice invoice = InvoiceFactory.Create(invoiceModel.Type);
    // 执行业务逻辑
    var calculateResult = invoice.Calculate(invoiceModel.Amount);
}

后续新增发票类型时,只需要新增IInvoice的实现类,再在字典中新增一条映射记录即可,无需修改原有业务逻辑。

方案2:反射自动发现(适合类型迭代频繁的场景)

如果项目中发票类型迭代频繁,可通过特性标记+反射扫描的方式完全省略手动维护映射的步骤:

  1. 首先定义类型标记特性
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class InvoiceTypeAttribute : Attribute
{
    public string TypeCode { get; }
    public InvoiceTypeAttribute(string typeCode) => TypeCode = typeCode;
}
  1. 为所有发票实现类添加特性标记
[InvoiceType("invoice")]
public class Invoice : IInvoice
{
    public double Calculate(double amount) => amount + 10;
}

[InvoiceType("discountinvoice")]
public class DiscountInvoice : IInvoice
{
    public double Calculate(double amount) => amount - 10;
}
  1. 程序启动时扫描所有实现自动构建映射
public static class InvoiceFactory
{
    private static readonly Dictionary<string, Type> _invoiceTypeMap;

    static InvoiceFactory()
    {
        _invoiceTypeMap = AppDomain.CurrentDomain.GetAssemblies()
            .SelectMany(a => a.GetTypes())
            .Where(t => t.IsAssignableTo(typeof(IInvoice)) && !t.IsInterface && !t.IsAbstract)
            .Select(t => new
            {
                Type = t,
                Attr = t.GetCustomAttribute<InvoiceTypeAttribute>()
            })
            .Where(t => t.Attr != null)
            .ToDictionary(t => t.Attr.TypeCode, t => t.Type, StringComparer.OrdinalIgnoreCase);
    }

    public static IInvoice Create(string invoiceType)
    {
        if (_invoiceTypeMap.TryGetValue(invoiceType, out var type))
        {
            return (IInvoice)Activator.CreateInstance(type);
        }
        throw new NotSupportedException($"未适配的发票类型:{invoiceType}");
    }
}

该方案下后续新增发票类型只需要新增实现类并添加InvoiceType特性即可,不需要修改工厂的任何代码,完全符合开闭原则。

补充说明

如果实例不需要每次新建,可将字典的Value改为单例实例,减少重复创建对象的性能开销;如果项目使用了依赖注入容器,也可将实例的创建逻辑交由容器管理,直接从容器中解析对应类型的实例即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 04:36:02