I recently started working on a project that does test-first development in C++. I have mostly done TDD in Java, but I've done a lot of C++ before that so neither the language nor methodology is new to me. However, after two-three years of unit testing in Java and a few scripting languages, I have (luckily) learned a few things. So this time around I hope to avoid one of the problems I had earlier with C++: the problem with ownership of newed objects, that is, which object owns another dynamically allocated object?.
When I was rewriting some classes to be testable I, as you usually do, introduced interfaces, used factories, injected dependencies, etc. In one case I rewrote the class under test (CUT) to use a factory instead of calling new directly. This made it easy to test that the CUT allocated an object correctly. But how to test that the CUT deletes that allocated object?
Well, it was quite simple: simply add a method to the factory called destroy that takes one argument which is the object to be destroyed. The method destroy is used to tell the factory I'm done with this object, do what ever you like with it... delete it it you want to. The destroy method corresponds to the make method, which of course allocates an object.
This is probably not some great new discovery I've made; most of you who have done test-first development in languages with manual memory management have probably already done this kind of thing. This was a new thought for me, though.
I also realized how clear the ownership of the object (created by the factory) had become. It was obvious from reading the production code and/or test-case that it was the factory that owned the object; all it did was to lend the object to some other class until destroy was called. This was an insight for me.
The fact that the factory owns the objects it creates means that it is trivial to replace the memory allocation scheme used by the factory. Only the factory is needed to be changed, e.g., if there is a need for pooling objects. Awesomeness.
I guess there are cases when this approach is not possible to use, but in cases where it is I think its a good pattern to use.
No comments:
Post a Comment