使用子类为Kotlin变量赋值:可替换默认工厂实现问题
解决可替换默认工厂的类型适配问题
看起来你遇到的是工厂模式里的典型痛点:既要让工厂支持自定义替换,又得给一个开箱即用的默认实现,还不想写那种满是硬编码类型判断的丑陋代码对吧?
先按常见场景模拟你的基础类/接口(毕竟你没贴具体代码,我尽量贴近通用情况):
// 规格接口 public interface ISpec { } // 具体规格实现 public class ConcreteSpec : ISpec { } // 产品接口 public interface IProduct { } // 具体产品实现 public class ConcreteProduct : IProduct { }
你提到的“丑陋可行方案”大概率是类似下面这种,靠object强转+分支判断来凑:
private IProduct UglyDefaultFactory(object spec) { if (spec is ConcreteSpec cs) return new ConcreteProduct(); throw new InvalidCastException("不支持的规格类型"); }
下面给你几种更优雅的实现方式,既满足“可替换默认工厂”的需求,又能摆脱丑陋的类型判断:
方案1:用动态类型简化默认逻辑
既然你能接受运行时崩溃,用dynamic可以完美避免硬编码分支,代码清爽很多:
public class ProductFactoryHolder { private readonly Func<ISpec, IProduct> _factory; // 默认工厂委托,用dynamic自动匹配对应重载 private static IProduct DefaultFactory(dynamic spec) { return CreateProduct(spec); } // 针对具体Spec的创建逻辑,新增类型只需要加新重载 private static ConcreteProduct CreateProduct(ConcreteSpec spec) { return new ConcreteProduct(); } // 构造函数支持传入自定义工厂,没传就用默认实现 public ProductFactoryHolder(Func<ISpec, IProduct> customFactory = null) { _factory = customFactory ?? (spec => DefaultFactory(spec)); } // 对外暴露的创建入口 public IProduct Create(ISpec spec) { return _factory(spec); } }
这种方式的优势是:默认逻辑拆分清晰,新增ISpec实现时只需要添加对应的CreateProduct重载,完全不用修改工厂核心逻辑,比硬转object优雅太多。
方案2:泛型约束限定默认类型(适合单一默认场景)
如果你的默认场景只绑定某一对ISpec和IProduct,可以用泛型约束做编译期的基础检查:
public class GenericProductFactory<TSpec, TProduct> where TSpec : ISpec, new() where TProduct : IProduct, new() { private readonly Func<ISpec, IProduct> _factory; private static IProduct DefaultFactory(ISpec spec) { if (spec is TSpec) return new TProduct(); throw new InvalidOperationException($"仅支持{typeof(TSpec).Name}类型的规格"); } public GenericProductFactory(Func<ISpec, IProduct> customFactory = null) { _factory = customFactory ?? DefaultFactory; } public IProduct Create(ISpec spec) { return _factory(spec); } }
使用时直接实例化var factory = new GenericProductFactory<ConcreteSpec, ConcreteProduct>(),默认逻辑就绑定好了,换类型只需要修改泛型参数。
方案3:字典映射维护多类型默认关系
如果有多种默认的ISpec→IProduct对应关系,用字典维护映射比一堆if-else/switch更易扩展:
public class MappedProductFactory { private readonly Func<ISpec, IProduct> _factory; // 预注册默认类型映射,新增类型直接加条目 private static readonly Dictionary<Type, Func<IProduct>> _defaultMappings = new() { { typeof(ConcreteSpec), () => new ConcreteProduct() } }; private static IProduct DefaultFactory(ISpec spec) { if (_defaultMappings.TryGetValue(spec.GetType(), out var creator)) return creator(); throw new KeyNotFoundException($"找不到{spec.GetType().Name}对应的产品创建逻辑"); } public MappedProductFactory(Func<ISpec, IProduct> customFactory = null) { _factory = customFactory ?? DefaultFactory; } public IProduct Create(ISpec spec) { return _factory(spec); } }
这种方式的扩展性拉满,新增规格和产品时只需要在字典里加一行映射,完全不用动工厂的核心逻辑。
不管选哪种方案,核心都是把默认工厂的逻辑从丑陋的硬编码类型判断里解放出来,同时保留自定义工厂的可替换性——完美匹配你“默认能用、可替换、接受运行时崩溃”的需求。
内容的提问来源于stack exchange,提问作者TheHebrewHammer
相关产品推荐
相关产品推荐

