我必须妥协:DRY还是Command-Query-Separation?


10

我最近正在重构既是命令又是查询方法的方法。

将其分为一个命令方法和一个查询方法后,我发现代码中现在有多个地方可以调用命令,然后从查询中获取值,这似乎违反了DRY原理。

但是,如果我要将通用代码包装到一个方法中,则该方法既是命令又是查询。这可以接受吗?


好的,我不知道社区是否达成共识,也找不到关于此主题的任何讨论。
克里斯·威尔士

它的通常称为CQRS google.com.au/...
丹尼尔·利特尔

@DanielLittle-不,不是。CQS和CQRS是截然不同的主题。CQRS是一种涉及更多的体系结构模式,而CQS则更多是设计模式,并且更易于掌握和实施。参见codebetter.com/gregyoung/2009/08/13/command-query-separation
Erik Funkenbusch 2015年

@Erik Funkenbusch您说得对
Daniel Little

Answers:


11

在相互矛盾的设计原则之间总要权衡考虑。解决问题的方法是查看原则背后的根本原因。在这种情况下,在不运行命令的情况下无法运行查询是有问题的,但是在不运行查询的情况下无法运行命令通常是无害的。只要有一种独立运行查询的方法,我就没有理由不将查询结果添加到命令中,特别是如果执行以下操作:

QueryResult command()
{
   // do command stuff
   return query();
}

4

我之前从未听说过Command-Query-Separation(CQS),但似乎它与Single Responsibility Principle(SRP)有关,SRP指出理想情况下,一个功能/类只应负责做一件事和一件事。

如果您的命令代码是20行代码,而查询代码是另外30行,并且它们全部在一个函数体中,那么显然您违反了SRP,我也假定CQS,并且这两个逻辑应该彼此分开。

但是,以您的假设示例为例,我很可能会创建一个包装器方法,该方法将您的命令和查询结合在一起,以便在代码的很多地方都不会违反DRY。我也不会认为这是违反SRP(也许是CQS)的,因为包装器仍然只有一个职责:将命令与查询结合起来并创建更易于使用的更高层次的抽象。

我认为包装器方法是完全可以接受的解决方案,为了说明这一点,让我们将您的示例更进一步。如果必须运行2个查询而不是1个查询,然后根据该命令执行命令操作,该怎么办。因此,您的2行代码将是6或8。如果在彼此之间进行一些数据验证/检查,该怎么办,所以现在您有15行代码。您是否会考虑创建一个可以完成所有操作的包装程序,而不是将这15行分散在多个文件中?


我认为包装器的“单一原则”应该是将需要命令和查询的其他方法保持在一起。
Droogans


虽然Karl对于这个问题的解决方案更好,但我确实发现您对较长的包装器函数的详细说明是很不错的。
克里斯·威尔士

-3

DRY更重要,因为它解决了更为根本的需求-避免了多余的有效工作。这是一件基本的事情-不需要成为程序员就可以理解它。

CQS是对不支持跟踪效果的语言难以理解其结果和效果均已执行的代码的一种回应。然而:

  1. 无法避免为了获得结果而执行代码的必要性,因为这是从小单元组成大型程序的基础。

  2. 也不能避免执行代码以达到其效果的必要性,因为在数学和理论计算机科学之外,运行程序的价值在于它可以为我们做的事。

  3. 无法避免在同一代码中产生效果和产生结果的必要性,因为在实践中,我们不仅需要效果,而且还需要结构性。

当然,解决跟踪问题对于无人驾驶的人来说太难了,真正的解决办法是让计算机 帮助我们跟踪运行时值之间的复杂关系(例如数组索引的有效性)可以说是类似的事情,对于这些异常关系,异常和运行时强制执行的合同构成了(非)解决方案。

总之,诸如CQS之类的“解决方案”仅会妨碍根据基于现实的合理原则设计程序。去干。


有时您需要避免耦合以降低复杂性。您应该看看CQRS。
Daniel Little

@Lavinski:避免复杂性(不降低复杂性,这是徒劳的)的最佳工具是抽象-将我们正在解决的问题的本质从所述一般问题的实例的特定细节中分离出来。不可思议的配方(或我所说的“设计模式”)充其量只能防止您将设计弄错时对您造成太大的损害,但它们不能将错误的设计变成正确的设计。
2014年

@Lavinski:特别是对于CQR​​S,在概念上正确的替代解决方案是1.理解数据模型(没有任何对象层可以消除这一点的必要性),2.在数据库模式中编码尽可能多的正确性属性。(可悲的是,最流行的RDBMS对后者提供了相当有限的支持,更不用说NoSQL了,这使这个更加错误。我目前的研究正在为此提供更好的解决方案。)
2013年

CQRS的工作原理与领域驱动设计完全一致。建议您做一些研究。应用程序内部的域应该强制正确性,而不是您的数据存储。
丹尼尔·

1
@EduardoLeón:如果您想证明设计是正确的,请尝试为程序编写测试。我可以向您保证,抛出CQS只会妨碍您的努力。
Stefan Billiet
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.