我听说不遵守面向对象设计中SOLID原则的最常见论点之一是YAGNI(尽管论证者通常不称之为):
“我可以将功能X和功能Y放在同一个类中。这很简单,为什么还要添加一个新类(即复杂性)。”
“是的,我可以将所有业务逻辑直接放入GUI代码中,它更容易,更快。这将是唯一的GUI,并且不太可能出现重要的新需求。”
“如果在不太可能出现新要求的情况下,我的代码变得过于混乱,我仍然可以重构新的要求。所以你的'如果你以后需要......那么论点'不会计算。”
你最反对这种做法的最有说服力的论据是什么?我怎么能显示这是一种昂贵的做法,特别是对那些没有太多软件开发经验的人来说。
答案 0 :(得分:44)
设计是权衡的管理和平衡。 YAGNI和SOLID没有冲突:前者说当添加功能时,后者说如何,但他们都指导设计过程。我在下面对每个特定报价的回答都使用了YAGNI和SOLID的原则。
- 构建可重用组件的难度是单次使用的三倍 组件。
- 应该尝试三种不同的可重用组件 之前的应用程序将足够通用以接受重用 库。
醇>- Robert Glass'Rules of Three, Facts and Fallacies of Software Engineering
重构为可重用组件的关键要素是首先在多个位置找到相同的目的,然后 移动它。在这种情况下,YAGNI通过在需要时内联该目的而应用,而不必担心可能的重复,而不是添加通用或可重用的功能(类和函数)。
在初始设计中,YAGNI不适用的最佳方式是确定具体要求。换句话说,在编写代码之前进行一些重构,以证明重复不仅仅是可能的,而且已经存在:这证明了额外的努力。
是的,我可以将所有业务逻辑直接放入GUI代码中,这样更容易,更快捷。这将永远是唯一的GUI,并且不太可能出现重要的新要求。
它真的是唯一的用户界面吗?是否计划了后台批处理模式?会不会有网络界面?
您的测试计划是什么,您是否将在没有GUI的情况下测试后端功能?什么使GUI易于测试,因为您通常不希望测试外部代码(例如平台通用GUI控件),而是专注于您的项目。
我可以将功能X和功能Y放在同一个类中。这很简单,为什么要添加一个新类(即复杂性)。
你能指出一个需要避免的常见错误吗?有些事情很简单,例如对一个数字(x * x
vs squared(x)
)进行平方以获得一个过于简单的例子,但是如果你能指出一个人犯的具体错误 - 特别是在你的项目中或那些在您的团队中 - 您可以展示一个共同的类或功能将来如何避免这种情况。
如果在不太可能出现新要求的情况下,我的代码太乱了,我仍然可以重构新的要求。所以你的“如果你以后需要......会怎么样”这个论点不算数。
这里的问题是“不太可能”的假设。你同意这不太可能吗?如果是这样,那么你就同意这个人的意见。如果没有,你对设计的想法与这个人不一致 - 解决这种差异会解决问题,或者至少告诉你下一步该去哪里。 :)
答案 1 :(得分:6)
听起来你正在用砖墙争论。我是YAGNI的忠实粉丝,但与此同时,我也希望我的代码总是至少在两个地方使用:应用程序和测试。这就是为什么像UI代码中的业务逻辑这样的东西不起作用的原因;在这种情况下,您无法测试UI代码的业务逻辑。
然而,根据您所描述的回答,听起来这个人对做更好的工作根本不感兴趣。那时,没有任何原则可以帮助他们;他们只想尽可能做到最低限度。我甚至可以说YAGNI不是在驾驶他们的行为,而是懒惰,而你一个人不会打败懒惰(几乎没有什么可以,除了威胁经理或失去工作)。
答案 2 :(得分:6)
我喜欢用半信半疑而不是半信半疑地考虑YAGNI,从37signals(https://gettingreal.37signals.com/ch05_Half_Not_Half_Assed.php)借用这句话。这是关于限制范围,以便您可以专注于做最重要的事情。这不是一个草率的借口。
GUI中的业务逻辑对我来说感觉不错。除非您的系统是微不足道的,否则如果您的业务逻辑和GUI已经多次独立更改,我会感到惊讶。因此,您应该遵循SRP(" S"在SOLID中)和重构 - YAGNI不适用,因为您已经需要它。
如果你今天要做额外的工作以适应假设的未来要求,关于YAGNI和不必要的复杂性的争论绝对适用。当那些"如果以后我们需要......"如果情景未能实现,那么您现在遇到的实际情况就会受到更多维护成本的影响。在这种情况下,我们正在谈论通过限制范围来简化设计 - 做一半,而不是半as。
答案 3 :(得分:3)
没有答案,或者更确切地说,你或你的对话者都没有答案:YAGNI和SOLID都是错误的方法。
尝试与没有经验的团队或具有严格交付目标的团队一起选择SOLID,这几乎可以保证您最终会得到一堆昂贵的,过度设计的代码......这些代码不会过于固定,只是过度设计(又欢迎来到现实世界)。
尝试去YAGNI进行长期项目并希望你以后能够重构只能在一定程度上起作用(也欢迎来到现实世界)。 YAGNI擅长概念证明和示威者,获得市场/合同,然后能够投入更多SOLID。
在不同的时间点你需要两者。
答案 4 :(得分:2)
这些原则的正确应用往往不是很明显,而且很大程度上取决于经验。如果你自己不这样做,很难获得。每个程序员都应该体验到做错的后果,但当然它总是应该是“不是我的”项目。
向他们解释问题是什么,如果他们不听,你不能让他们听,就让他们犯错误。如果您经常需要解决问题,那么您应该对自己的简历进行改进。
答案 5 :(得分:2)
根据我的经验,它始终是一种判断力。是的,你不应该担心你的实现的每一个细节,有时将一个方法粘贴到现有的类中是一个可接受的,虽然难看的解决方案。
你可以稍后重构。重要的是实际进行重构。所以我认为真正的问题不是偶尔的设计妥协,而是一旦明确出现问题就推迟重构。实际上经历它是困难的部分(就像生活中的许多事情一样......)。
关于你的个人观点:
我可以将两个功能X放在一起 并将Y作为同一个类。它 是如此简单,为什么要添加一个新的 阶级(即复杂性)。
我想指出,让一个类中的所有内容都更复杂(因为这些方法之间的关系更贴心,更难理解)。拥有许多小班并不复杂。如果你觉得列表变得很长,只需将它们组织成包,你会没事的:-)。就个人而言,我发现只需将一个类分成两个或三个类就可以提供很多可读性,而无需进一步改变。
不要害怕小班,不要咬人; - )。
是的,我可以把我所有的业务逻辑 它直接进入GUI代码就很多了 更容易,更快捷。这将永远 是唯一的GUI,它是高度的 不太可能是新的重要 要求将永远存在。
如果有人可以说“不太可能出现重要的新要求。”直面,我相信那个人真的,真的需要现实检查。要生硬,但要温柔......
如果在新的情况下不太可能 要求我的代码也得到了 我仍然可以为此重构 新要求。那么你的'如果你怎么样 后来需要...'论证没有 计数
这有一些优点,但前提是它们实际上稍后会进行重构。所以接受它,并遵守他们的承诺: - )。
答案 6 :(得分:2)
SOLID原则允许软件适应变化 - 在需求和技术变化(新组件等)中,您的两个论点是不变的要求:
这真的是真的吗?
在各种发展费用方面,没有经验可以替代。对于许多从业者而言,我认为以糟糕,难以维护的方式做事从未给他们带来过问题(嘿!工作保障)。从产品的长期来看,我认为这些费用变得清晰,但提前做些事就是别人的工作。
这里还有其他一些很好的答案。
答案 7 :(得分:2)
可以理解,灵活且能够修复和改进的内容总是 需要的东西。实际上,YAGNI假设您可以相对轻松地返回并添加新功能,因为没有人会像在类中的不相关功能(在该类中使用YAGNI!)或将业务逻辑推送到UI逻辑那样疯狂。
有时候,现在似乎疯狂的事情在过去是合理的 - 有时候UI与业务的界限或者应该在不同类别中的不同责任集之间的边界线并不是那么清晰,甚至不会移动。有时候2小时内绝对需要3小时的工作时间。有些时候人们只是没有做出正确的电话。由于这些原因,偶尔会出现这方面的突破,但它们会妨碍使用YAGNI原则,而不是原因。
答案 8 :(得分:2)
质量单元测试,我的意思是单元测试而不是集成测试,需要遵守SOLID的代码。不一定是100%,实际上很少这样,但在你的例子中,将两个特性填充到一个类中将使单元测试更难,打破单一责任原则,并使团队新手更难以理解代码维护(因为它更难理解) 。
通过单元测试(假设良好的代码覆盖率),您将能够重构功能1安全可靠,您不会破坏功能2,但没有单元测试和同类功能(简单在你的例子中懒惰)重构充其量是冒险的,充其量是灾难性的。
答案 9 :(得分:1)
tldr;
SOLID假设,您了解(稍微至少)代码的未来更改,即SRP。我会说,对预测能力持乐观态度。 另一方面,YAGNI假设大多数时候你不知道未来的变化方向,这对预测能力持悲观态度。
因此,它遵循SOLID / SRP要求您为代码形成类,以便它具有单一的更改原因。例如。一个小的GUI更改或ServiceCall更改。
YAGNI说(如果你想在这种情况下强制应用它),因为你不知道什么会改变,如果GUI改变会导致GUI + ServiceCall改变(类似的后端改变导致GUI + SeviceCall更改),只需将所有代码放在单个类中。
答案很长:
阅读“敏捷软件开发,原则,模式和实践”一书
我简要介绍一下SOLID / SRP: “如果应用程序没有以导致两个职责在不同时间发生变化的方式发生变化,则无需将它们分开。实际上,将它们分开会闻到不必要的复杂性。”
这里有一个证据。只有在发生变化时,变化轴才是变化轴。如果没有任何症状,应用SRP或任何其他原则是不明智的。“