mardi 30 décembre 2014

How to fake C++ classes containing non-virtual functions?

I'm trying to bring some C++ legacy code under test. In particular, I have a class hierarchy, say, A < B < C (i.e., A is the superclass of B, and B is the superclass of C), and there is a global reference to an object of type C which is used from all over the system's code (singleton pattern). The goal is to replace that C object with some fake object (in fact, C is used to access a database).


My first attempt was to introduce interfaces IA, IB, and IC (which contain pure virtual versions of the functions of the corresponding class), let each class implement its interface, and change the type of the global C reference to IC. In the setup function of my tests, I would then replace the globally referenced C object with my own implementation of IC, making the whole system use my fake implementation.


However, classes A, B, and C each contain quite a few non-virtual functions. Now, if I would make the classes inherit from my interfaces, I would change the semantics of these functions from non-virtual to virtual (Feathers discusses this problem in "Working efficiently with legacy code", p.367). In other words: I have to check each and every call to my global object, and I have to make sure that after my changes, still the same functions are called. This sounds like a LOT of ERROR PRONE work to me.


I also thought about making the non-virtual functions "final", i.e., tell the compiler that the functions of A, B and C must not be hidden in subclasses (which would make the compiler tell me all potentially dangerous functions of B and C - if a function is not hidden in a base class, the above effect can not happen at all), but that doesn't seem to be supported by C++ (we are not yet using C++11, but even its final keyword only seems to be applicable to virtual functions).


To make the situation even more difficult, classes A, B, and C also contain public attributes, virtual functions, and also some template functions.


So my question is: How to cope with the situation I described above? Are there any C++ capabilities that I have missed, and which could help in my scenario? Any design patterns? Or even any refactoring tools? My main requirement is that the changes must be as safe as possible, since the classes I'd like to fake are rather crucial to the system... I would also be happy with an "ugly" solution which would allow me to put tests into place (and which could be refactored later if the system is appropriately covered with tests).


Aucun commentaire:

Enregistrer un commentaire