
Scott Meyers wrote:
EventLog::EventLog(std::ostream& logstream); // an ostream must be // specified
I've slept soundly with this philosophy for many years, but lately I've noticed that this idea runs headlong into one of the ideas of testability: that classes should be easy to test in isolation. The above constructor requires that an ostream be set up before an EventLog can be tested, and this might (1) be a pain and (2) be irrelevant for whatever test I want to perform. In such cases, offering a default constructor in addition to the above would make the class potentially easier to test. (In the general case, there might be many parameters, and they might themselves be hard to instantiate for a test due to required parameters that their constructors require....)
The key here is that mock/stub objects in C++ are in some way a pain. In your example, what are you going to test with your event log that won't need to be validated by checking the logstream? In my cases I've used regex matching on the logstream, even in my constructor test case I verify that nothing is written to the stream, so I always supply a fake ostream - usually a string based stream that I can validate. I know there was some degree of push back in the blog-o-sphere of over use of this form of Inversion of control (see http://www.martinfowler.com/articles/injection.html for some of the alternatives)
Another thing I've noticed is that some users prefer a more exploratory style of development: they want to get something to compile ASAP and then play around with it. In particular, they don't want to be bothered with having to look up a bunch of constructor parameters in some documentation and then find ways to instantiate the parameters, they just want to create the objects they want and then play around with them. My gut instinct is not to have much sympathy for this argument,
Personally I'd prefer the use of Null objects mostly because I'm objecting to code like this: value myclass:method() { if (member_variable == is_not_set) return NULL_OF_SOME_FORM; // do something with member_variable } slightly better might be: value myclass:method2() { if (member_variable == is_not_set) throw something; // do something with member_variable } but even better is code that reads value myclass:method3() { // do something with member_variable } It fits better with the idea of value objects and makes me more likely to write code that has a stronger exception guarantee, but thats me :-)
So I'm confused. Constructors that "really" initialize objects detect some kind of errors during compilation, but they make testing harder, are arguably contrary to exploratory programming, and seem to contradict the advice of the designers of the .NET API. Constructors that "sort of" initialize objects are more test-friendly (also more loosely coupled, BTW) and facilitate an exploratory programming style, but defer some kinds of error detection to runtime (as well as incur the runtime time/space costs of such detection and ensuing actions).
So I guess I'd add the fact that a constructor IMO should be used to initialize an object into a well defined state including setup of the object's invariant. Two phase construction runs the risk that you might forget something. My view is that objects with large number of parameters passed into the constructor smells of a missing object/responsibility in the design, even if its a data transfer object, my making such a class your trying to direct the programmer to not forget something. Testability isn't really effected by single phase construction. In my experience it helps because all your setup code is in 'one line', not spread about. These are of course guidelines and not hard and fast rules. Kevin -- | Kevin Wheatley, Cinesite (Europe) Ltd | Nobody thinks this | | Senior Technology | My employer for certain | | And Network Systems Architect | Not even myself |