Unit tests are a great thing.
Today I’ve committed a revised version of the implementation of the ActiveRecord pattern, which saves instances of objects in SQLite databases. This new architecture replaces the “Curiously Recurring Template Pattern” used before, where ActiveRecord inherited (via template parameters) of the BelongsTo and HasMany classes:
Client classes used to have the following (ugly) syntax in their declarations; watch out for the brackets at the end of the declaration, and the duplication of one of the parameters in the old HasMany declaration!:
The whole thing was rather obscure and hard to understand, and even ugly, and I was looking for a different way to do things.
Hopefully, today I stumbled upon the entry #25.10 of the C++ FAQ Lite, “What does it mean to “delegate to a sister class” via virtual inheritance?” and I refactored the whole family of classes, which now look like this (check out the new ActiveRecord, BelongsTo and HasMany classes):
And this means that now, client classes are defined in this (much more readable) way:
The Persistable class is an “Abstract Base Class”, providing (via public virtual inheritance) a common interface to all the template classes, which finally tremendously simplified the code. ABCs are better defined by as defined by Meyers:
Bill Venners: Let’s define ABC. ABC means abstract base class. So what would you call in C++ that special case of an ABC that has no data and nothing but pure virtual functions, which have no implementation.
Scott Meyers: The C++ community does not have a name for that. There is no commonly accepted term for that kind of class. Some people call it “interface class.” Some people quite incorrectly call it a virtual base class, but as long as we are among friends and know what we are talking about, that’s OK. But the notion of essentially a C++ implementation of an interface in Java or C# does not have a name in C++.
Of course, this whole refactoring thing would not have been possible without the complete suite of 32 unit tests, testing most code interfaces in a straightforward way. This has helped me complete a rather obscure refactoring in a very short amount of time, with the certainty that the code still does what it should do. When the tests ran without throwing errors, it meant that I had been able to keep the interfaces intact, and to provide a whole new architecture to the framework.
And that’s simply great.