68

我听到的关于在面向对象设计中不遵守SOLID原则的最常见论点之一是YAGNI(尽管争论者通常不这么称呼它):

“我可以将特征 X 和特征 Y 放在同一个类中。这很简单,为什么要麻烦添加一个新类(即复杂性)。”

“是的,我可以将我所有的业务逻辑直接放入 GUI 代码中,这样更容易、更快捷。这将永远是唯一的 GUI,而且不太可能出现重大的新需求。”

“如果在不太可能出现新需求的情况下,我的代码变得过于混乱,我仍然可以针对新需求进行重构。所以你的‘如果你以后需要……怎么办’的论点不重要。”

反对这种做法的最有说服力的论点是什么?我如何才能真正证明这是一种昂贵的做法,尤其是对于那些在软件开发方面没有太多经验的人。

4

10 回答 10

52

设计是权衡的管理和平衡。 YAGNI 和 SOLID 并不冲突:前者说明何时添加功能,后者说明如何添加功能,但它们都指导设计过程。我在下面对您的每个具体报价的回复都使用了 YAGNI 和 SOLID 的原则。

  1. 构建可重用组件的难度是一次性组件的三倍。
  2. 一个可重用的组件应该在三个不同的应用程序中试用,然后它才能足够通用以接受到重用库中。

  ——罗伯特·格拉斯的三法则软件工程的事实和谬误

重构为可重用组件的关键要素是首先在多个地方找到相同的目的,然后再移动它。在这种情况下,YAGNI 通过在需要的地方内联该目的来应用,而不用担心可能的重复,而不是添加通用或可重用的特性(类和函数)。

在初始设计中,显示 YAGNI 何时不适用的最佳方式是确定具体需求。换句话说,在编写代码之前进行一些重构,以表明重复不仅是可能的,而且已经存在:这证明了额外的努力是合理的。


是的,我可以将所有业务逻辑直接放入 GUI 代码中,这样更容易、更快捷。这将始终是唯一的 GUI,并且极不可能出现重要的新需求。

它真的是唯一的用户界面吗?是否计划了后台批处理模式?会有网页界面吗?

你的测试计划是什么,你会在没有 GUI 的情况下测试后端功能吗?什么会使 GUI 易于测试,因为您通常不想测试外部代码(例如平台通用 GUI 控件),而是专注于您的项目。

我可以将特征 X 和特征 Y 放在同一个类中。为什么要添加一个新类(即复杂性)是如此简单。

你能指出一个需要避免的常见错误吗?有些事情很简单,例如将数字平方(x * xvs squared(x))作为一个过于简单的例子,但如果你能指出某人犯的具体错误——尤其是在你的项目中或你的团队中的人——你可以展示一个常见的错误类或函数将来会避免这种情况。

如果在不太可能出现新需求的情况下,我的代码变得过于混乱,我仍然可以针对新需求进行重构。所以你的“如果你以后需要……怎么办”论点不算数。

这里的问题是“不太可能”的假设。你同意这不太可能吗?如果是这样,你就同意这个人了。如果不是,您的设计理念与此人的理念不一致——解决这种差异将解决问题,或者至少告诉您下一步该往哪里走。:)

于 2010-10-13T16:46:30.807 回答
9

我喜欢用“一半,而不是一半”来考虑 YAGNI,借用 37signals 的短语(https://gettingreal.37signals.com/ch05_Half_Not_Half_Assed.php)。这是关于限制你的范围,这样你就可以专注于做好最重要的事情。这不是马虎的借口。

GUI 中的业务逻辑让我觉得半途而废。除非您的系统是微不足道的,否则如果您的业务逻辑和 GUI 尚未独立更改几次,我会感到惊讶。因此,您应该遵循 SRP(SOLID 中的“S”)并重构 - YAGNI 不适用,因为您已经需要它。

如果您今天正在做额外的工作以适应假设的未来需求,那么关于 YAGNI 和不必要的复杂性的论点绝对适用。当那些“如果以后我们需要......”的场景未能实现时,您将陷入抽象的更高维护成本中,这些抽象现在阻碍了您实际拥有的更改。在这种情况下,我们讨论的是通过限制范围来简化设计——只做一半,而不是半途而废。

于 2014-09-15T01:58:21.030 回答
7

听起来你在和一堵砖墙争论。我是 YAGNI 的忠实粉丝,但同时,我也希望我的代码至少会在两个地方使用:应用程序和测试这就是为什么 UI 代码中的业务逻辑之类的东西不起作用的原因。在这种情况下,您无法单独测试 UI 代码的业务逻辑。

但是,从您所描述的回答来看,听起来这个人只是对做得更好的工作不感兴趣。那时,没有任何原则可以帮助他们。他们只想做尽可能少的事情。我什至要说,不是 YAGNI 推动了他们的行动,而是懒惰,而且只有你自己无法打败懒惰(几乎没有什么可以战胜的,除了威胁性的经理或失去工作)。

于 2010-10-13T12:45:20.883 回答
3

没有答案,或者更确切地说,有一个您和​​您的对话者都不喜欢的答案:YAGNI 和 SOLID 都可能是错误的方法。

尝试与缺乏经验的团队或交付目标严格的团队一起使用 SOLID 几乎可以保证你最终会得到一堆昂贵的、过度设计的代码......这不会是 SOLID,只是过度设计(又名欢迎到现实世界)。

尝试将 YAGNI 用于一个长期项目并希望您以后可以重构只能在一定程度上起作用(也就是欢迎来到现实世界)。YAGNI 擅长概念验证和演示,获得市场/合同,然后能够投资于更可靠的东西。

你需要两者,在不同的时间点。

于 2017-03-30T14:19:23.533 回答
2

这些原则的正确应用往往不是很明显,很大程度上取决于经验。如果你不自己做,这是很难获得的。每个程序员都应该经历过做错的后果,但当然它应该永远是“不是我的”项目。

向他们解释问题出在哪里,如果他们不听,而你又不能让他们听,那就让他们犯错误。如果你经常需要解决问题,你应该完善你的简历。

于 2010-10-13T08:51:25.853 回答
2

根据我的经验,这始终是一个判断电话。是的,您不应该担心实现的每一个小细节,有时将方法粘贴到现有类中是可以接受的,尽管是丑陋的解决方案。

确实可以稍后重构。重要的一点是实际进行重构。所以我相信真正的问题不是偶尔的设计妥协,而是一旦发现有问题就推迟重构。实际上经历它是困难的部分(就像生活中的许多事情一样......)。

至于你的个人观点:

我可以将特征 X 和特征 Y 放在同一个类中。为什么要添加一个新类(即复杂性)是如此简单。

我要指出的是,将所有东西都放在一个类中会更复杂(因为方法之间的关系更密切,也更难理解)。拥有许多小班并不复杂。如果您觉得列表越来越长,只需将它们组织成包,就可以了 :-)。就个人而言,我发现只需将一个类分成两个或三个类就可以大大提高可读性,而无需任何进一步的更改。

不要害怕小班,他们不咬人;-)。

是的,我可以将所有业务逻辑直接放入 GUI 代码中,这样更容易、更快捷。这将始终是唯一的 GUI,并且极不可能出现重要的新需求。

如果有人可以说“重大的新要求极不可能出现”。板着脸,我相信那个人真的,真的需要现实检查。直言不讳,但温柔...

如果在不太可能出现新需求的情况下,我的代码过于混乱,我仍然可以针对新需求进行重构。所以你的“如果你以后需要……怎么办”的论点不算数

这有一些优点,但前提是他们稍后确实进行了重构。所以接受它,并让他们信守承诺:-)。

于 2010-10-13T10:22:16.830 回答
2

SOLID 原则允许软件适应变化 - 在需求和技术变化(新组件等)中,您的两个论点是针对不变的需求:

  • “极不可能出现重大的新要求。”
  • “如果在不太可能出现新要求的情况下”

这真的是真的吗?

当涉及到各种开发费用时,经验是无可替代的。对于许多从业者来说,我认为以糟糕、难以维护的方式做事从来没有给他们带来问题(嘿!工作保障)。从产品的长期来看,我认为这些费用会变得很清楚,但提前做一些事情是别人的工作。

这里还有其他一些很棒的答案。

于 2010-10-13T19:08:33.153 回答
2

易于理解、灵活且能够修复和改进始终是您需要的东西。实际上,YAGNI 假设您可以在证明有必要时相对轻松地返回并添加新功能,因为没有人会做一些疯狂的事情,例如在类中插入不相关的功能(该类中的 YAGNI!)或将业务逻辑推向 UI 逻辑.

有时现在看起来很疯狂的事情在过去是合理的——有时 UI 与业务的边界线或应该属于不同类的不同职责集之间的界限并不那么清晰,甚至是移动的。有时候,在 2 小时的时间内绝对需要 3 小时的工作。有时人们只是没有做出正确的决定。由于这些原因,在这方面偶尔会发生中断,但它们会妨碍使用 YAGNI 原则,而不是它的原因。

于 2010-10-14T09:21:23.823 回答
2

质量单元测试,我的意思是单元测试而不是集成测试,需要遵循 SOLID 的代码。不一定是 100%,实际上很少如此,但在您的示例中,将两个功能塞入一个类会使单元测试更难,打破单一责任原则,并使团队新手的代码维护更加困难(因为它更难理解) .

通过单元测试(假设代码覆盖率良好),您将能够安全可靠地重构功能 1,您不会破坏功能 2,但没有单元测试并且具有同一类中的功能(只是在您的示例中懒惰)重构充其量是有风险的,充其量是灾难性的。

底线:遵循 KIS 原则(保持简单),或者为知识分子遵循 KISS 原则(kis 愚蠢)。考虑每个案例,没有全局答案,但始终考虑其他编码人员将来是否需要阅读/维护代码以及每个场景中单元测试的好处。

于 2016-07-29T22:59:11.173 回答
1

tldr;

SOLID 假设您了解(至少有点)未来对代码的更改,wrt SRP。我会说这是对预测能力持乐观态度。另一方面,YAGNI 假设大多数时候你不知道未来的变化方向,这对预测能力是悲观的。

因此,SOLID/SRP 要求您为代码形成类,以便它有单一的更改理由。例如,小的 GUI 更改或 ServiceCall 更改。

YAGNI 说(如果您想在这种情况下强制应用它),因为您不知道会发生什么变化,以及 GUI 更改是否会导致 GUI+ServiceCall 更改(类似于导致 GUI+ServiceCall 更改的后端更改) ,只需将所有代码放在单个类中即可。

长答案:

阅读《敏捷软件开发、原则、模式和实践》一书

我从中摘录了关于 SOLID/SRP 的简短摘录:“如果,[...]应用程序没有以导致两种职责在不同时间发生变化的方式发生变化,则无需将它们分开。确实,将它们分开会闻到不必要的复杂性。

这里有一个推论。只有当变化发生时,变化轴才是变化轴。如果没有症状,应用 SRP 或任何其他原则是不明智的。”

于 2014-09-04T06:28:14.953 回答