
Scott Meyers wrote:
Joel de Guzman wrote:
One-phase construction definitely! Testing in isolation is a different matter and should not degrade the interfaces. There are ways to allow isolated testing without degrading the interface. It all boils down to decoupling and isolating dependencies. My favorites:
1) Use a template where policies can be replaced by hooks to the testing engine that tests the expected results. In your example, I'd imagine this interface: template <class Printer> EventLog.
Or the more conventional OO approach of subclassing from ostream (in the example) and passing in a null or mock derived class object for testing purposes.
Your approach has the drawback that it's now more difficult to have a container of all EventLog objects (because Printer is part of the type) and the OO approach has the drawback of requiring the introduction of a base class and virtuals in cases where they might otherwise not be necessary. To modify the example, suppose the EventLog constructor requires a Widget, and Widget is a large nonpolymorphic object with no virtuals. I'd still pass the Widget by reference, but subclassing it for testing would be ineffective, due to the lack of virtuals.
Either way the desire to make the class testable affects the interface seen by users. This is not a complaint, just an observation. In another post, I noted that it seems like it'd be nice to be able to somehow create a "testing only" interface separate from the "normal" client interface.
2) Use callbacks. In the example you provided, I'd imagine EventLog calls logstream to print. So, I'd use a constructor like: EventLog::EventLog(boost::function
print) instead. So, instead of calling logstream << stuff directly, I'll call print(stuff). For the testing engine, I'll replace it with something that tests the expected results. All these falls under the "Hollywood Principle: Don't call us, we'll call you". IMO, with proper design, you can have both single phase construction *and* isolation testing.
But can you also have maximal inlining and, where needed by clients, runtime polymorphism? Templates preserve inlining but tend to sacrifice polymorphism (e.g., it's hard to have a container of (smart) pointers to EventLog<T> objects for all possible Ts), while base class interfaces and callbacks preserve polymorphism at the expense of easy inlining.
How many Ts (for all EventLog<T>) do you need? For deployment in an application, surely the set of Ts is bounded. If there is a need to put them in a container, I'd place them in a tuple or a fusion::set, or if you have more than one instances of each, a tuple or a fusion::set of std::vector(s). Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net