如何基于参数类型实现工厂模式,消除Photo类判断的冗余if条件
C# 消除Photo类型判断的实现方案
可以通过工厂模式实现,除此之外还有多类适配不同场景的优雅实现方案,具体如下:
方案1:动态分发(最小改动方案)
利用C#的dynamic关键字将重载决议从编译期推迟到运行期,自动匹配对应派生类的Load重载,仅需修改一行代码即可完全消除所有if类型判断:
public void ImportPhoto(PhotoBase photo) { _photoLoader.Load((dynamic)photo); }
优缺点
- 优点:改动量极小,无需修改原有基类、派生类、PhotoLoader的结构,新增派生类时仅需在PhotoLoader中添加对应重载即可自动适配
- 缺点:存在极微小的动态类型性能开销,无编译期类型校验,若新增派生类后未添加对应
Load重载,仅会在运行期抛出异常
方案2:访问者模式(类型安全的OOP标准方案)
如果追求编译期类型安全、无额外性能开销,可以采用访问者模式实现多态分发:
第一步:修改抽象基类,定义访问者接口
public abstract class PhotoBase { public string Path { get; set; } // 新增接收访问者的抽象方法 public abstract void Accept(IPhotoVisitor visitor); } // 定义访问者接口 public interface IPhotoVisitor { void Visit(FilePhoto photo); void Visit(ExternalPhoto photo); // 后续新增派生类时,仅需在此处添加对应Visit方法定义 }
第二步:派生类实现Accept方法
public class FilePhoto : PhotoBase { public override void Accept(IPhotoVisitor visitor) => visitor.Visit(this); } public class ExternalPhoto : PhotoBase { public override void Accept(IPhotoVisitor visitor) => visitor.Visit(this); }
第三步:PhotoLoader实现访问者接口
public class PhotoLoader : IPhotoVisitor { public void Visit(FilePhoto Photo) { // 原有从文件系统加载的逻辑 } public void Visit(ExternalPhoto Photo) { // 原有从URL下载的逻辑 } }
第四步:修改ImportPhoto逻辑
public void ImportPhoto(PhotoBase photo) { photo.Accept(_photoLoader); }
优缺点
- 优点:完全类型安全,编译期即可校验所有派生类是否有对应处理逻辑,无额外性能开销,符合开闭原则
- 缺点:需要修改原有基类和派生类的结构,新增派生类时需要同步修改访问者接口的定义
方案3:工厂+策略模式(无需修改原有类结构的工厂实现)
就是你提到的工厂模式的落地实现,将类型判断逻辑收敛到PhotoLoader内部的策略字典中,完全不需要修改基类和派生类:
public class PhotoLoader { // 存储类型对应加载策略的字典 private readonly Dictionary<Type, Action<PhotoBase>> _loadStrategies = new(); public PhotoLoader() { // 注册各类型的加载逻辑 RegisterStrategy<FilePhoto>(LoadFilePhoto); RegisterStrategy<ExternalPhoto>(LoadExternalPhoto); } // 泛型注册方法,避免重复转型代码 private void RegisterStrategy<T>(Action<T> loadAction) where T : PhotoBase { _loadStrategies.Add(typeof(T), photo => loadAction((T)photo)); } // 对外统一的Load入口 public void Load(PhotoBase photo) { if (_loadStrategies.TryGetValue(photo.GetType(), out var loadAction)) { loadAction(photo); return; } throw new NotSupportedException($"不支持的照片类型:{photo.GetType().Name}"); } // 原有各类型的加载逻辑,改为私有方法 private void LoadFilePhoto(FilePhoto Photo) { // 从文件系统加载 } private void LoadExternalPhoto(ExternalPhoto Photo) { // 从URL下载 } }
修改后ImportPhoto可以直接调用统一入口:
public void ImportPhoto(PhotoBase photo) { _photoLoader.Load(photo); }
优缺点
- 优点:无需修改原有基类、派生类的结构,类型安全,无动态类型开销,新增派生类时仅需添加对应加载方法并在构造函数中注册即可
- 缺点:需要手动注册策略,漏注册会在运行期抛出异常
方案选型建议
- 小型项目、快速迭代优先选动态分发方案,改动成本最低
- 中大型项目、派生类数量稳定优先选访问者模式,类型安全更有保障
- 不能修改基类/派生类结构的场景优先选工厂+策略模式,适配性最强
内容的提问来源于stack exchange,提问作者Hooman Bahreini
相关产品推荐
相关产品推荐

