|
Boost Users : |
From: Scott Meyers (usenet_at_[hidden])
Date: 2006-09-14 01:17:36
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<void(std::string const&)> 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.
Scott
Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net