7

我开始编写流畅的界面,并查看了 Martin Fowler 写的关于流畅界面的旧文章(我没有意识到他和 Eric Evans 创造了这个术语)。在这篇文章中,Martin 提到 setter 通常会返回正在配置或处理的对象的实例,他说这违反了 CQS。

花括号世界中的常见约定是修饰符方法是无效的,我喜欢它,因为它遵循 CommandQuerySeparation 的原则。这个约定确实妨碍了流畅的界面,所以我倾向于暂停这种情况下的约定。

因此,如果我的流利界面执行以下操作:

myObject
  .useRepository("Stuff")
  .withTransactionSupport()
    .retries(3)
  .logWarnings()
  .logErrors();

这真的违反了CQS吗?

更新我打破了我的示例,将日志记录警告和错误显示为单独的行为。

4

4 回答 4

9

是的。所有这些方法显然都在返回一些东西,同样显然它们有副作用(从你没有对返回值做任何事情的事实来看,但你确实费心去调用它们)。由于 CQS 的定义规定 mutators 不应该返回一个值,因此我们手中有一个明确的违规行为。

但是,违反 CQS 对您来说重要吗?如果流畅的界面让您在所有方面都更加高效,并且如果您认为它是一种众所周知的模式,具有同样众所周知的优点和缺点,那么它在纸上违反原则 X 有什么关系呢?

于 2012-03-16T18:06:56.787 回答
3

当它改变对象而不是只返回一个新对象时,它违反了这个原则。

var newObject = myObject
    .useRepository("Stuff")
    .withTransactionSupport()
    .retries(3)
    .logWarningsAndErrors(); 

如果myObject在此语句之后没有变化,则一切正常。一般来说,一个流畅的接口违反了 CQS 原则,当且仅当它有副作用时。

但是问题是,如果您的示例确实代表了一个查询。“流利”是否一定意味着“查询”?它可能只是被视为一个动作流畅的界面,其中相同的对象从一个动作传递到下一个动作。

于 2012-03-16T18:07:27.380 回答
0

不,这里的模式是“配置”。此类配置命令返回配置对象本身,与与命令无关的内容相反。如果服务于配置目的的命令返回一些不相关的数据,则会发生违反命令/查询隔离的情况,例如:

if (myObject.UseRepository("Stuff") > 1 && myObject.UseRepository("Bla") < 5) {
    // oh, good, some invisible stuff internal to myObject is in right interval...
}
于 2012-03-16T18:08:00.750 回答
0

我认为这取决于这些方法在做什么。如果每个都是它自己的命令,那么是的,它可能会破坏 CQS。

但是,您可以通过 2 种不同的方式轻松解决此问题。

  1. 只是不要链接命令。只需执行 myObject.useRepository("..")。然后调用下一个,等等。但是如果链中的下一个项目需要前一个项目的信息,那么您将遇到麻烦。

  2. 这些链接的东西并没有让它们中的每一个成为自己的命令,而是直接更新 DTO 上的数据。然后在最后,您运行一个名为 .Configure() 的方法,然后将此 DTO 发送到执行所有处理的单个命令。

于 2020-05-18T18:42:08.480 回答