I've been working on a personal project for some time, and I believe that I'm at that point where, if I don't embrace unit tests (and ideally TDD) now, it may be too late, so I want to make the right call. I'll try to keep things generic, so that this answer can be useful to as many people as possible.
Something relevant to note is that I'm using C++ and my system is built on top of a third party framework, as I know that most of this problems can be quite different in other environments.
I found a proposal for achieving good isolation that has a ton of obvious benefits, but I think that, if followed, the risk of falling into the class proliferation anti-pattern is quite high. I'll focus on the impact in my application structure, not so much on the testing side, since that doesn't concern me (I'm fine with the structure there).
The proposal I found says that, for every framework class used, you have to create:
- An interface, so that it can be mocked when doing unit testing.
- An adapter implementing that interface with the framework functionality.
The second point is to decouple logic from the place it is used. For doing so, for each class of my domain or system, I will now have:
- Class A containing the logic, which will operate using the framework interfaces created. Hence, it will be framework agnostic and receive its members through constructor input parameters (dependency injection principle).
- Class B, which will make use of class A and that knows everything about the framework (well, what it needs to know about it).
With this setup, B creates an instance of an adapter for each object that A needs as input parameter to it's constructor so that A can use it for carrying out its logic.
So, to summarize, I will now have to create two classes per framework class I use, and 2 classes per class of my domain.
I'm totally sold on the benefits this approach has, but it's also clear that the amount of parts on the system will sky rocket. From my point of view, this could end up in a class proliferation anti-pattern where I have to fight with much more modules, much more files and much more classes.
I know that I could loosen this approach a bit, such as not follow the decoupling process for classes that are only for internal use inside the module they belong to, or letting some dependencies from the framework leak into the classes that implement the logic (such as basic data structures). But I fear doing this will end up making my system too heterogeneous, with modules following totally different patterns/structures and making them harder to understand by the same person (me).
- Am I missing something here that makes me think the risk of class proliferation is higher than it really is?
- Is there any other approach I could follow to achieve similar results with less classes/modules?
- Or do you think that the class proliferation is worth the ability to do proper, isolated unit testing and TDD? If so, is the possibility of loosening the isolation a bit acceptable? Do you know any other way of loosening the decoupling besides the ones I mentioned that can be useful?