'Are fluent interfaces a violation of the Command Query Separation Principle?

I started writing a fluent interface and took a look at an older piece Martin Fowler wrote on fluent interfaces (which I didn't realize he and Eric Evans coined the term). In the piece, Martin mentions that setters usually return an instance of the object being configured or worked on, which he says is a violation of CQS.

The common convention in the curly brace world is that modifier methods are void, which I like because it follows the principle of CommandQuerySeparation. This convention does get in the way of a fluent interface, so I'm inclined to suspend the convention for this case.

So if my fluent interface does something like:

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

Is this truly a violation of CQS?

UPDATE I broke out my sample to show logging warnings and errors as separate behaviors.



Solution 1:[1]

No. The pattern here is "Configuration". Such configuration commands return the configuration object itself as opposite to something unrelated to the command. A violation of the Command/Query segregation would occur if the commands which serve the configuration purpose returned some unrelated data, for example:

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

Solution 2:[2]

Yes, it is. All those methods are obviously returning something, and equally obviously they have side effects (judging from the fact that you don't do anything with the return value, yet you do bother to call them). Since the definition of CQS states that mutators should not return a value we have a clear-cut violation in our hands.

But does it matter to you that CQS is violated? If the fluent interface makes you more productive all things considered, and if you consider it a well-known pattern with equally well-known benefits and drawbacks, why should it matter that it violates principle X on paper?

Solution 3:[3]

It violates this principle when it changes objects but not when it only returns a new object.

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

If myObject is unchanged after this statement, everything is OK. Generally spoken, a fluent interface violates the CQS principle, if, and only if it has side effects.

However the question is, if your example does represent a query at all. Does "fluent" necessarily mean "query"? It could probably just be perceived as an action-fluent-interface where the same object is passed from one action to the next.

Solution 4:[4]

I think it depends on what those methods are doing. If each one is it's own command, then yes, it could be breaking CQS.

However, you could fix this easily 2 different ways.

  1. Just don't chain the commands. Just do myObject.useRepository(".."). Then call the next one, etc. But if the next item in the chain requires information from the previous one you would be in trouble.

  2. Instead of making each of these their own command, instead these chained things are simply updating data on the DTO directly. Then at the end, you run a method called .Configure() that then sends this DTO to a single command that does all of the processing.

Solution 5:[5]

If we ignore the type system DSL design, fluent interface is exactly the same as method chaining.

o.A().B() is equivalent to a = o.A(); a.B()

It does violate command-query separation in a mutable data structure. We have to explicitly add superfluous return this in method implementation (Here a & o refer to the same object) (By the way I prefer method cascading in such case)

However, we also often see it in immutable data structure, because a pure function has to return the result. (Here a & o refer to different objects`) In this case it does not violate command-query separation

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Jon
Solution 3
Solution 4 Daniel Lorenz
Solution 5 colinfang