C# 中的 DRY 原则:为什么代码重复会损害您的代码库--由 Derek Comartin 解释
当我们谈论用 C# 编写可维护代码时,一个经常浮现的基本概念就是 DRY 原则 - Don't Repeat Yourself。 它是软件开发的支柱,旨在消除冗余、减少代码重复并提高代码的可维护性。
但是,与许多设计原则一样,DRY 也可能被误解甚至误用。 来自 CodeOpinion.com 的 Derek Comartin 在他的视频"DRY 原则是您的代码库糟糕的原因?"中坦率而务实地探讨了 DRY 原则应该和不应该如何使用--尤其是在 .NET Core 或类似生态系统中开发时。
在本文中,我们将深入探讨 Derek 的解释,通过他的示例和视频中的评论进行分析。 无论您是要在 Visual Studio 中启动一个新项目,还是要维护现有的代码库,或者只是要重构代码以提高代码的可重用性,Derek 的见解都非常实用和贴切。
DRY 原则的定义
在视频的开头,Derek 框定了许多开发人员所面临的环境:处理一个难以修改的系统--重复代码和冗余逻辑组成的一团乱麻。
他介绍了 C# 中的 DRY 原则,将其作为减少代码重复的一种策略,但警告说这一原则经常被曲解。 正如德里克在 0:28 所解释的那样:
当 DRY 成功应用时,对系统中任何一个元素的修改都不需要对任何逻辑上不相关的元素进行修改"。
这一区别至关重要。 DRY 原则的目的不仅在于避免重复代码,还在于促进关注点的分离和适当的代码重用--从而产生更易于测试和重构的可维护、干式代码。
实用示例:距离转换
为了让事情具体化,Derek 用 C# 提供了一个简单的例子。 他编写了两种方法:
船舶距离
- 收费距离
每种方法都以英里为单位计算距离,然后将其转换为公里--两种方法使用相同的逻辑。 这是典型的代码重复。
Derek 演示了如何将转换逻辑提取到一个私有方法--MilesToKilometers() 中,而不是在多个地方使用同一段代码--这是一种基本但有效的重构代码以实现可重用性的方法。
他使用典型的控制台应用程序结构来说明这一点:一个带有静态 void Main 的 Program 类。 许多开发人员在测试逻辑或尝试新的用户输入场景(如公共 int 年龄、字符串用户名、字符串密码等)时都会使用这种结构。
DRY 与过度耦合
虽然将逻辑抽象为可重复使用的方法或单独的类听起来很理想,但 Derek 呼吁大家谨慎行事。 过度使用 DRY,尤其是在整个应用程序中过度使用 DRY,会导致危险的耦合。
例如,如果您将转换逻辑放入多个项目使用的共享工具中,然后更改其四舍五入行为或十进制精度,那么这一更改可能会意外地影响许多领域。 正如德里克在 2:31 所说:
"客户是否希望有两位小数? 如果我们把它改成零会怎么样?
这是一个跨领域的问题--逻辑被系统的许多部分重复使用--它说明了过早集中或没有明确界限的风险。
Derek 的建议与 "单一责任原则"(Single Responsibility Principle)和 "依赖反转原则"(Dependency Inversion Principle)不谋而合,而这两项 SOLID 原则对于保持代码的适应性和模块化至关重要。
错误的 DRY 导致的代码膨胀
DRY 误用的另一个问题是代码臃肿--试图抽象一切导致实用类臃肿或方法过于通用。 Derek 警告说,过度干燥的逻辑可能弊大于利,尤其是在大型系统中,由于共享依赖关系,一个领域的错误修复可能会破坏其他领域。
Derek 认为,关键是要知道什么时候不应该共享代码,尤其是在导致模块紧密耦合的情况下。 DRY 并非规则; 这是一份必须结合上下文使用的指南。
DRY 应用于实体:复杂性的秘诀
Derek 指出了开发人员的一个共同倾向:完全围绕卡车、订单、司机和装运等实体来组织系统。 在不同的方法中重复使用相同的类或对象很有诱惑力,但这往往会导致概念重复和不必要的耦合。
他认为,驱动架构的应该是业务能力,而不仅仅是数据结构。 例如,"派送订单 "与 "卸下拖车 "的关注点不同,即使它们涉及相同的实体。
4:45 时,Derek 解释道:
"您系统中的单一实体不需要代表多个概念"。
这凸显了一个更深层次的架构见解:具有相同名称的实体(车辆、拖车)在不同的工作流中可能代表不同的职责。 交替使用会造成混淆,并将不相关的业务逻辑紧密联系在一起。
DRY 和业务能力
为了解决这个问题,Derek 引入了垂直切片架构(VSA)--一种围绕业务能力而不是层来构建应用程序的模式。 每个 "片段 "都包括特定操作或用例所需的所有内容--从请求到数据库--封装且自成一体。
他强调,DRY 代码在片段内(单个位置内)是好的,但跨片段应用 DRY 会导致依赖关系纠缠不清。 在 6:44 处,他补充道
"这只是为了降低耦合度、增加内聚力... 做到这一点的方法之一是:不要在边界内重复概念"。
这种边界驱动思维为您提供了灵活性。 您可能在一个片段中有一个完整的领域模型,而在另一个片段中只有一个轻量级数据模型。 这取决于切片的需求--一种与《务实的程序员》理念相一致的务实方法。
最后的想法
最后,Derek 将 DRY 重新定义为一种工具,而不是法律。 正如他在 7:00 时所说的那样:
"这只是了解你是如何应用它的。 如果你大量应用它,你可能会有更多的耦合"。
因此,在提取验证逻辑、连接字符串或将重复代码转换为单独的方法之前,请考虑这样做是否会使您的整个代码库更易于维护,或者只是更难修改。
结论
Derek Comartin 在 C# 中对 DRY 原则的分解说明了一条看似简单的规则在没有细微差别的情况下是如何适得其反的。 通过讲解代码示例、讨论实际应用场景和强调软件设计原则,他揭示了可重用性和模块化之间所需的平衡。
请记住,要想大大提高您的开发进程,您必须
使用 DRY 在明确的边界内重构冗余代码。
不要 DRY 了服务于不同业务目的的实体。
在集中逻辑时要尊重上下文,尤其是跨多个地方或项目的逻辑。
- 考虑单元测试以及依赖注入如何影响共享代码。
通过应用这些经验,您将编写出更高效、模块化和可维护的 C# 代码,并避免将您的代码库变成由重复逻辑和纠缠不清的依赖关系组成的老鼠窝。
您可以在 CodeOpinion 的 YouTube 频道上观看 Derek Martin 的完整视频,了解更多见解。

