核心结论
你说得完全正确:就你当前「图层前缀统一过滤」这个场景,完全没必要拆成两个独立委托参数、写两份一模一样的过滤代码。分开做两套参数属于过度设计。
下面把底层逻辑拆开讲清楚:
1、什么时候适合分开两个委托(pipeFilter / pointFilter)
只有一种场景值得分开:管线、管点的过滤规则不一样。
举几个现实例子:
- 拓扑回溯时:管线要求前缀=ws,但是管点不受图层限制,任何图层的节点都可以作为中转;
- 管线要校验XData管网类型,管点只校验图层;
- 禁止穿过雨水管线,但是雨水节点可以被选中,不阻断路径。
管线过滤有一个非常特殊的身份:它同时承担「结果筛选」+「链路阻断器」两个职责
而原先代码里的管点过滤,仅仅只是:这个点要不要放进结果集合,不能阻断遍历前进。
这也是当初我分开两个参数的根本原因 —— 二者在回溯流程里作用位置、产生的影响完全不同,即使过滤条件碰巧一样,语义角色不一样。
2、什么时候完全可以只用1个统一过滤函数
当你的业务规则满足:
管线、管点,使用完全一模一样的判断条件。
这时你只需要传一个委托,管线校验用它、管点校验也复用它一份即可。
不需要新增第二个形参。
3、还有一个很关键的语义区别(最容易踩坑)
管线过滤 → 不满足 → continue;链路切断,上游不再往前走(影响遍历路径) 管点过滤 → 不满足 → continue;只是这个点不加入结果,但是遍历路径不受任何影响哪怕判断函数代码一模一样,二者产生的后果不是一回事。
举个例子:
链路 ysA‑ys管‑ws节点‑ys管‑ysB
- 如果管线不过滤、只给管点加图层前缀过滤:
ws节点不会被加到结果,但是队列仍然会穿过ws节点,跑到另一边 ys‑B。 - 如果管线过滤:遇到ws管线直接停止,整条路断掉。
所以:函数代码相同 ≠ 在回溯里产生的效果相同。
4、给你最优的取舍思路
方案A(适合你现在:图层前缀过滤)
只增加单个可选委托参数Func<ObjectId,bool> entityFilter
管线判断时调用它;收集管点的时候,也调用同一个委托。
一份逻辑,两处复用。简洁够用,没有冗余。
方案B(预留扩展,面向未来复杂规则)
保留两个独立参数 pipeFilter / pointFilter。
- 当前调用:传同一个委托实例进去,两份代码不用重复写;
- 以后哪天管线、管点过滤逻辑分叉了,你不用再去改 TraceUpstream 的函数签名、到处修改调用代码。
代价就是接口多了一个参数,属于为未来功能预留的扩展设计。
5、对你当前项目的建议
现阶段图层前缀过滤,选方案A,单委托就足够,不需要双参数。
等到后面你出现:管线限制条件 和 管点限制条件不一样的时候,再拆分成两个独立过滤委托即可。
如果你需要,我后面可以给你改成单‑委托版本的回溯代码。