I recently used a library that allows the following type of syntax:
MyClass myObject;
myObject
.setMember1(\"string value\")
.setMember2(4.0f)
.s
Some people call this fluent programming (or a fluent interface). Others call it a mess.
I tend somewhat toward the latter camp. In particular, my experience has been that in many cases, people writing code this way depend on the "fluent interface" for quite a bit of initializing an object. In other words, despite the disguise, it's still two-step initialization. Likewise, although it's probably avoidable in many cases, it frequently seems to result in quite a bit of what should be entirely private to the class being made publicly modifiable via manipulators.
Personally I prefer that objects are immutable after creation. That's clearly not always possible, and in some cases you can't even come very close. Nonetheless, the more of an object's internals that you make open to outside manipulation, the less certain you become about that object maintaining a coherent state (and, typically, the more work you have to do to maintain a coherent state).
There's no problem with this style. The only downside is that you can't use the return value for more typical purposes, like returning the result of the function.
In theory, you could end up with a dangling reference if you do something awful like:
MyClass *myObject = new MyClass;
MyClass & dangling = myObject->setMember1("string");
delete myObject;
dangling.setMember2(yrParam);
So be aware of that.
This is sometimes referred to as the Named Parameter Idiom or method chaining. This isn't bad practice, it can aid readability. Consider this example lifted from the C++ FAQ
File f = OpenFile("foo.txt")
.readonly()
.createIfNotExist()
.appendWhenWriting()
.blockSize(1024)
.unbuffered()
.exclusiveAccess();
The alternative would be to use positional arguments to the OpenFile method, requiring the programmer to remember the position of each argument.
Your example is not the named parameters idiom.
With the named parameters idiom, the chainable setters set attributes of an argument (parameter) pack.
Your code instead has a bunch of setters for modifying the final object you're constructing, called two-phase construction.
In general two-phase construction is just Bad™, and two-phase construction implemented by exposing attributes to client code, as in your example, is Very Bad™.
For example, you do not, in general, want to be able to modify a file object's attributes, once that object has been constructed (and possibly file opened).
Cheers & hth.,
It's a common practice. The overloading of operator=
implies to do so to chain calls:
class Foo {
public:
Foo& operator=(const Foo& f) {
if (this != &f) { // check for self-assignment
// do some stuff...
}
return *this;
}
};
This code allows to do things like:
Foo a, b, c;
a = b = c;
Please note that checking for self-assignment is compulsory because you often have to deallocate stuffs in the current object, so allowing a = a
would break your code.
Following @Fred Nurk's comment, I'd like to add that you should have a look at the Copy-and-Swap idiom in order to avoid code duplication and issue an exception-free code.
Have a look at the links hereunder for more information:
What is the copy-and-swap idiom?
http://gotw.ca/gotw/059.htm