流畅的界面是否违反命令查询分离原则?

时间:2012-03-16 18:00:34

标签: c# fluent-interface command-query-separation

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

  

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

所以如果我的流畅界面做了类似的事情:

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

这是否真的违反了CQS?

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

4 个答案:

答案 0 :(得分:9)

是的,确实如此。所有这些方法显然都返回了一些东西,同样显然它们有副作用(从你没有对返回值做任何事情的事实来判断,但你还懒得打电话给它们)。由于CQS的定义表明变异者不应该返回一个值,我们手中有明确的违规行为。

但是CQS被违反了吗?如果流畅的界面可以让你考虑所有事情,并且如果你认为它是一个众所周知的模式,具有同样众所周知的优点和缺点,为什么应该它是否违反了纸上的原则X?

答案 1 :(得分:3)

它在更改对象时违反了这个原则,但在它只返回一个新对象时却没有。

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

如果此语句后myObject未更改,则一切正常。一般来说,如果且仅当它具有副作用时,流畅的界面违反了CQS原则。

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

答案 2 :(得分:0)

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

if (myObject.UseRepository("Stuff") > 1 && myObject.UseRepository("Bla") < 5) {
    // oh, good, some invisible stuff internal to myObject is in right interval...
}

答案 3 :(得分:0)

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

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

  1. 仅不要链接命令。只需执行myObject.useRepository(“ ..”)。然后呼叫下一个,依此类推。但是,如果链中的下一个项目需要上一个项目的信息,那么您会遇到麻烦。

  2. 这些链接起来的事情不是直接使它们各自成为命令,而是直接在DTO上更新数据。然后最后,您运行一个名为.Configure()的方法,然后将该DTO发送到执行所有处理的单个命令。