实例化POCO类代码如何重构?POCO集合依赖耦合优化方案咨询
核心结论
你提到的「尽量避免在类中使用new关键字」的规范,适用场景是有业务逻辑、有外部依赖、需要支持单元测试替换实现的服务类,完全不适用纯数据POCO和类内部的临时状态集合。你想到的前两个方案属于典型的过度设计,第三个方案也存在逻辑漏洞,最优的重构方案如下:
现有问题排查
首先你当前的代码存在两个隐性bug:
_visitedBranch作为类字段,若CustomerCountRequestProcessor以DI框架默认的单例模式注册,多次请求会共享同一个集合,上次请求的访问记录会导致本次请求误判循环分支、异常报错。- 在
GetCustomersForSelectedNode中重写类字段_network的操作存在并发风险,多线程同时调用时会互相覆盖_network的值,导致计算结果错误。
不合理方案说明
- 「封装集合类+接口注入」:完全没有必要,
_visitedBranch是单请求内的临时状态,不需要对外暴露也不需要替换实现,注入反而会因为生命周期管理不当引发共享状态冲突。 - 「工厂类获取集合」:工厂的作用是封装复杂的实例创建逻辑、控制实例生成策略,你仅需要一个空的存储集合,没有复杂创建逻辑,工厂属于冗余代码。
- 「集合初始化放构造函数」:依然没有解决单例模式下多请求共享集合的问题,只是把初始化位置移动了,核心bug没有修复。
最优重构方案
把访问集合从类字段改为单请求内的局部变量,用HashSet替代List提升存在性判断的性能(HashSet.Contains是O(1)复杂度,List.Any是O(n)复杂度),同时修正异常类型、解决并发问题:
public class CustomerCountRequestProcessor : ICustomerCountRequestProcessor { private readonly INetwork _defaultNetwork; public CustomerCountRequestProcessor(INetwork defaultNetwork) { _defaultNetwork = defaultNetwork ?? throw new ArgumentNullException(nameof(defaultNetwork)); } public int GetCustomersForSelectedNode(IRoot request) { if (request == null) throw new ArgumentNullException(nameof(request)); var currentNetwork = request.Network ?? _defaultNetwork; // 每次请求新建独立的访问记录集合,和其他请求完全隔离 var visitedBranches = new HashSet<(int StartNode, int EndNode)>(); return ProcessSelectedNode(request.SelectedNode, currentNetwork, visitedBranches); } private int ProcessSelectedNode(int node, INetwork network, HashSet<(int StartNode, int EndNode)> visitedBranches) { var nodeCustomer = network.Customers.FirstOrDefault(c => c.Node == node); var customersForNode = nodeCustomer?.NumberOfCustomers ?? 0; foreach (var branch in network.Branches.Where(b => b.StartNode == node)) { var branchKey = (branch.StartNode, branch.EndNode); if (visitedBranches.Contains(branchKey)) { // 原逻辑抛出ArgumentNullException不符合错误场景,改为循环分支对应的异常类型 throw new InvalidOperationException($"检测到循环分支:{branch.StartNode} -> {branch.EndNode}"); } visitedBranches.Add(branchKey); customersForNode += ProcessSelectedNode(branch.EndNode, network, visitedBranches); } return customersForNode; } }
如果一定要保留独立的Branch类,只需要给Branch重写Equals和GetHashCode方法,或者给HashSet传入自定义的IEqualityComparer<Branch>即可,不需要额外引入抽象层。
内容的提问来源于stack exchange,提问作者user16405900
相关产品推荐
相关产品推荐

