如何优化枚举检查与仓储方法调用?SOLID开闭原则合规方案
这个问题我之前也碰到过——用switch处理多类型分支确实会违反开闭原则,每次新增ReturnType都得修改原有的switch块,维护起来特别麻烦。你想到用字典映射的思路是完全正确的,只是需要调整一下来适配不同的返回类型,下面给你两个实用的解决方案:
方案一:策略模式+泛型接口(推荐,更符合SOLID)
这种方式通过抽象出数据获取的通用接口,把每个类型的逻辑封装到独立的类中,完美遵循开闭原则。
首先定义一个通用的数据提供接口:
public interface IDataProvider<T> { IEnumerable<T> GetData(); }
然后为每个ReturnType实现对应的提供类,比如HR类型:
public class HRDataProvider : IDataProvider<HR> { private readonly IRepo _repo; // 通过构造注入仓储实例 public HRDataProvider(IRepo repo) => _repo = repo; public IEnumerable<HR> GetData() => _repo.GetSystemManuals(); }
同理,为Finance、Dev等类型实现对应的IDataProvider:
public class FinanceDataProvider : IDataProvider<PrivateRecord> { private readonly IRepo _repo; public FinanceDataProvider(IRepo repo) => _repo = repo; public IEnumerable<PrivateRecord> GetData() => _repo.GetPrivateRecords(); } // 其他类型的Provider以此类推...
接下来在你的业务类中,创建ReturnType到IDataProvider实例的映射字典:
public class YourBusinessService { private readonly IDictionary<ReturnType, object> _providerMap; public YourBusinessService(IRepo repo) { _providerMap = new Dictionary<ReturnType, object> { { ReturnType.HR, new HRDataProvider(repo) }, { ReturnType.Finance, new FinanceDataProvider(repo) }, { ReturnType.Dev, new DevDataProvider(repo) }, { ReturnType.Admin, new AdminDataProvider(repo) }, { ReturnType.Support, new SupportDataProvider(repo) } }; } // 通用的获取数据方法 public IEnumerable<T> GetData<T>(ReturnType returnType) { if (_providerMap.TryGetValue(returnType, out var provider) && provider is IDataProvider<T> dataProvider) { return dataProvider.GetData(); } throw new ArgumentOutOfRangeException(nameof(returnType), $"不支持的返回类型:{returnType}"); } }
调用的时候只需要明确指定返回类型即可:
var hrData = service.GetData<HR>(ReturnType.HR); var financeData = service.GetData<PrivateRecord>(ReturnType.Finance);
以后新增ReturnType时,只需要新增对应的IDataProvider实现,然后在字典里加一条映射即可,完全不需要修改原有逻辑,完美符合开闭原则。
方案二:简化版——用Func委托字典(快速实现,适合简单场景)
如果不想创建太多Provider类,可以直接用字典存储返回不同类型的委托,通过泛型方法做类型转换:
public class YourBusinessService { private readonly IDictionary<ReturnType, Func<object>> _dataGetterMap; public YourBusinessService(IRepo repo) { _dataGetterMap = new Dictionary<ReturnType, Func<object>> { { ReturnType.HR, () => repo.GetSystemManuals() }, { ReturnType.Finance, () => repo.GetPrivateRecords() }, { ReturnType.Dev, () => repo.GetTimeLine() }, { ReturnType.Admin, () => repo.GetLedger() }, { ReturnType.Support, () => repo.GetRoster() } }; } public IEnumerable<T> GetData<T>(ReturnType returnType) { if (_dataGetterMap.TryGetValue(returnType, out var getter)) { var result = getter(); if (result is IEnumerable<T> typedResult) { return typedResult; } throw new InvalidCastException($"返回类型无法转换为IEnumerable<{typeof(T).Name}>"); } throw new ArgumentOutOfRangeException(nameof(returnType)); } }
这种方式代码量更少,适合逻辑简单的场景,但扩展性比策略模式稍弱——新增类型时还是要修改字典的初始化代码(不过相比switch,修改范围更小,也更清晰)。
关键说明
两种方案的核心都是把分支逻辑从switch转移到外部映射,这样新增类型时不需要修改原有分支判断代码,只需要扩展映射内容,从而遵循开闭原则。如果你的项目用了依赖注入框架,还可以把所有IDataProvider实例注册到容器中,然后通过容器动态获取,进一步减少硬编码的映射。
内容的提问来源于stack exchange,提问作者Simsons
相关产品推荐
相关产品推荐

