7

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.

David Hoerster
  • 28,421
  • 8
  • 67
  • 102

5 Answers5

9

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?

Jon
  • 428,835
  • 81
  • 738
  • 806
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.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • hm, but the whole point of configuration is to change the object (subject to configuration) so why bother? – Paul Michalik Mar 16 '12 at 18:09
  • What difference does it make if object being modified is a new or an old instance? Any one of those 4 calls either modifies the return value of the expression (whatever that is) or does nothing and so should be omitted. – Jon Mar 16 '12 at 18:09
  • The question is, if it does modify an existing object and return a result at the same time, making it a command and a query at the same time. It should be either a command **or** a query. – Olivier Jacot-Descombes Mar 16 '12 at 18:27
0

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.

Daniel Lorenz
  • 4,178
  • 1
  • 32
  • 39
0

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

colinfang
  • 20,909
  • 19
  • 90
  • 173
0

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...
}
Paul Michalik
  • 4,331
  • 16
  • 18
  • 2
    sry, i don't understand your answer, but i want to. can you please expand it? "if you had..." had where? also, there is no altering of internal state in your snippet. i see the violation of "tell, don't ask" principle, but that's it – jungle_mole May 18 '15 at 03:10