固体与YAGNI

我所听到的最不经意的论据之一就是YAGNI (虽然论者经常不这样认为):

“把特征X和特征Y放到同一个类中是可以的,为什么还要添加一个新类(即复杂性)就这么简单。

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

“如果出现新的要求,我的代码变得太混乱,我仍然可以重新devise新的要求,所以你的'如果你以后需要…'这个参数不计算在内。

你最有说服力的反对这种做法的是什么? 我怎么能真正表明这是一个昂贵的做法,特别是对于没有太多软件开发经验的人来说。

devise是权衡的pipe理和平衡。 YAGNI和SOLID没有矛盾:前者说何时添加特征,后者是如何说的但他们都指导devise过程。 下面,我对每个具体的报价使用YAGNI和SOLID的原则。

  1. 构build可重复使用的组件作为一次性组件是三倍的难度。
  2. 一个可重用的组件应该在三个不同的应用程序中被试用,然后才能被接受到一个重用库中。

– 罗伯特·格拉斯的“三条规则” , 软件工程的事实和谬误

重构成可重用的组件首先在多个地方find相同的目的, 然后移动它。 在这种情况下,YAGNI通过在需要的地方插入目的来应用,而不用担心可能的重复,而不是添加通用或可重用的特性(类和函数)。

在最初的devise中,显示YAGNI何时不适用的最好方法是确定具体的要求。 换句话说, 在编写代码之前进行一些重构,以显示重复不仅是可能的,而且已经存在:这certificate了额外的努力。


是的,我可以把所有的业务逻辑直接放到GUI代码中,这样更容易,更快捷。 这将永远是唯一的graphics用户界面,不可能有重大的新需求。

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

你的testing计划是什么,你会在没有GUI的情况下testing后端function吗? 什么会使得GUI易于testing,因为你通常不希望在代码之外进行testing(比如平台通用的GUI控制),而是专注于你的项目。

把特征X和特征Y放到同一个类中是可以的。 这是如此简单,为什么要添加一个新的类(即复杂)。

你能指出一个需要避免的常见错误吗? 有些东西很简单,比如把一个数字( x * x vs squared(x) )打成一个过于简单的例子,但是如果你能指出某个人犯的一个具体的错误 – 尤其是在你的项目或你的团队中你可以展示一个普通的类或函数将来如何避免。

如果出现新的需求,我的代码变得太混乱,我仍然可以重新构造新的需求。 所以你的“如果你以后需要……”这个说法不算数。

这里的问题是“不太可能”的假设。 你同意这不太可能吗? 如果是这样,你与这个人是一致的。 如果不是这样,你的devise理念就不同意这个人的解决方法 – 解决这个矛盾将解决问题,或者至less告诉你下一步该去哪里。 🙂

这听起来像你在用砖墙争论。 我是YAGNI的忠实粉丝,但同时我也希望我的代码至less可以在两个地方使用:应用程序和testing。 这就是为什么像UI代码中的商业逻辑这样的东西不起作用; 在这种情况下你不能testing分离UI代码的业务逻辑。

然而,从你所描述的反应来看,这听起来就像这个人对做更好的工作毫无兴趣。 那时,没有什么原则可以帮助他们。 他们只想做最小的可能。 我甚至会说,这不是YAGNI驾驶他们的行为,而是懒惰,而且你一个人也不会打怠(几乎没有任何东西可以,除了一个有威胁的经理或者失去工作)。

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

graphics用户界面中的业务逻辑对我来说感觉一半。 除非你的系统是微不足道的,否则如果你的业务逻辑和graphics用户界面还没有独立地改变过几次,我会感到惊讶。 所以你应该遵循SRP(SOLID中的“S”)并重构–YAGNI不适用,因为你已经需要它了。

关于YAGNI和不必要的复杂性的争论绝对适用,如果你今天做了额外的工作来适应假设的未来需求。 当那些“如果以后我们需要…”情景不能实现的时候,那么现在阻碍了实际改变的抽象的维护成本会更高。 在这种情况下,我们正在讨论的是通过限制范围来简化devise – 做一半,而不是一半。

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

向他们解释问题是什么,如果他们不听,而你又不能让他们听,就让他们犯错。 如果你经常需要解决问题,你应该抛光你的简历。

根据我的经验,这总是一个判断力的呼唤。 是的,你不应该担心你的实现的每一个细节,有时坚持一个方法到现有的类是一个可以接受的,但丑陋的解决scheme。

确实,你可以稍后重构。 重要的一点是实际上做重构 。 所以我相信真正的问题不是偶尔的devise妥协,而是一旦变得清楚就有问题就推迟重构。 实际上,这是一个很难的部分(就像生活中的许多事情一样)。

至于你的个人观点:

把特征X和特征Y放到同一个类中是可以的。 这是如此简单,为什么要添加一个新的类(即复杂)。

我会指出,在一个类中的所有东西都是比较复杂的 (因为方法之间的关系更亲密,更难理解)。 有许多小class不复杂。 如果你觉得名单越来越长,把它们组织成包,你会没事的:-)。 就我个人而言,我发现只要将课堂分成两三个class级,可以提高可读性,而不需要做进一步的改变。

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

是的,我可以把所有的业务逻辑直接放到GUI代码中,这样更容易,更快捷。 这将永远是唯一的graphics用户界面,不可能有重大的新需求。

如果有人能说:“新的要求永远不可能进来”。 用一张张脸,我相信人真的, 真的需要现实检查。 生硬,但温柔…

如果在新的需求不太可能的情况下,我的代码变得太混乱,我仍然可以重新构造新的需求。 所以你的“如果你以后需要……”的论点不计算在内

这有一些好处,但只有当他们实际上在稍后重构。 所以接受它,并坚持他们的诺言:-)。

固体原则允许软件适应变化 – 在需求和技术变化(新组件等)中,你的两个论点是不变的要求:

  • “新的要求永远不会出现。”
  • 如果出现新的要求,

这真的可以吗?

当涉及到各种开发费用时,无法取代经验。 对于很多从业人员来说,我觉得做事情不好,难以维系,从来没有给他们带来什么问题(嘿,工作保障)。 从产品的长远来看,我认为这些开支是清楚的,但是提前做些事情就是别人的工作。

还有其他一些很好的答案。

可以理解的,灵活的,能够修复和改进的东西总是你需要的东西。 事实上,YAGNI假设你可以相对容易地回来添加新的function,因为没有人会做一些疯狂的事情,比如在类中join不相关的function(YAGNI在这个类中)或者把业务逻辑推到UI逻辑。

可能有些时候,现在看起来很疯狂的事情在过去是合理的 – 有时UI与业务之间的界限,或者不同类别的责任之间,应该处于不同的阶层之间,这种界限并不明显,甚至不是一个动作。 在2小时内绝对需要3小时的工作时间。 有些时候人们只是没有做出正确的决定。 由于这些原因,偶尔会有这方面的突破,但是它们会妨碍YAGNI原则的使用,而不是原因。

质量unit testing,我的意思是unit testing而不是集成testing,需要符合SOLID的代码。 不一定是100%,事实上很less如此,但是在你的例子中,将两个特性塞入到一个类中会使得unit testing变得更加困难,打破单一责任原则,并且使得团队新手更难以维护代码(因为难以理解) 。

通过unit testing(假设代码覆盖率很高),您将能够重构特性1安全可靠,不会中断特性2,但是不需要进行unit testing,并且可以在同一类中使用特性(在您的示例中只是懒惰)重构充其量也是危险的,充其量也是灾难性的。

底线:遵循KIS原则(简单点),或者KISS原则(kis愚蠢)。 以每个案例的优点,没有全局的答案,但总是考虑其他编码人员是否需要在将来读取/维护代码,以及在每种情况下unit testing的好处。

没有答案,或者说,你和对方都不会喜欢这个答案:YAGNI和SOLID都可能是错误的方法。

试图去找一个没有经验的团队的SOLID,或者一个有着严格交付目标的团队,几乎可以保证你得到一个昂贵的,过度devise的一堆代码…这不会是固定的,只是过度devise到现实世界)。

试图去YAGNI进行一个长期的项目,并希望以后可以重构只能在一定程度上(也就是欢迎现实世界)。 YAGNI擅长certificate概念和示威,获得市场/合同,然后能够投资更多的实体。

你需要在不同的时间点。

tldr;

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

因此,SOLID / SRP会要求您为代码组成类,以便它有单一的变更原因。 例如,一个小的GUI更改或ServiceCall更改。

YAGNI说(如果你想在这种情况下强制应用它),因为你不知道什么会改变,如果一个GUI改变将导致一个GUI + ServiceCall改变(类似地后端改变导致GUI + SeviceCall改变) ,把所有的代码放在一个类中。

很长的回答:

阅读“敏捷软件开发,原则,模式和实践”一书

我正在对SOLID / SRP做简短的摘录:“如果申请没有改变,导致两个责任在不同的时间发生变化,就没有必要把它们分开,事实上,把它们分开会闻到不必要的复杂性。

这里有一个佐证。 只有发生变化,变化轴才是变化的轴。 如果没有任何症状,那么应用SRP或任何其他原则并不明智。“