2

Currently I am working on C++ project, which can be classified as "legacy" project. A lot of poor written code was committed because of lack of experience and under the pressure of deadlines. When I try to fix bugs I eventually make the new one.

Recently I got some more free time and I want to make this project better. To be exact I want to begin using TDD. However, it turned out not so easy. I faced the problem that I can't write test for any class. Singletons are everywhere. Even basic classes uses static members.

For instance, in project we widely use "Numeric" class (which is wrapper for decNumber C library for decimal computation). This class has static member to keep a context (precision, rounding mode, etc.). It is obvious that I need to refactor this class to avoid static fields.

Immediately I thought about dependency injection and a factory pattern. The factory will keep the single context, every user class should create Numeric instances only using that factory. But the code is going to be messy because the factory should be injected in every class which wants to construct "Numeric" instance.

Which pattern I can use for this case? Which one makes me sure that every instance of "Numeric" class uses single shared state?

P.S.: There are significant parallels with this question - Dependency injection ; good practices to reduce boilerplate code.

2

I really like the book Working Effectively with Legacy Code, by Michael Feathers, for this purpose. I'll go ahead and disagree with gbjbaanb here; testing is very useful for legacy code, but you may need to approach it at a higher level than individual classes at first. Start by writing functional tests: mock out an object, send it through the system, and see if it comes out as expected on the other end. This process is painful, but necessary.

Once you have this sort of testing around the portions of the code that you want to refactor, you can begin making large-scale refactors, like taking static methods out of the classes you're testing, and then begin expanding the scope of your refactoring.

1

It depends what platform you're working on - Microsoft has Moles (called something else now) that allows you to replace objects in your program with other objects, so you can replace the singletons with ones specific to your test scenarios. Its wonderful and a damn sight easier to use than mangling your code to use interfaces everywhere so lesser unit test systems can work.

But, generally TDD is frowned upon for legacy code - as I mentioned above, when writing code designed to be test driven, you have to code it in a certain way. Not easy to do with existing codebase which is never written like that (which suggests to me that the TDD way of coding is not natural).

I would forget putting things like DI in, as you'll only make the system even more complicated and messy. You can change your static methods on your class, remove the static, make the constructor set a default, and allow the class to set different properties - though, if the numeric's static is always a constant value, then what's the point of changing it so each object carries the state rather than the class? Just so you can use TDD? When you do this, you're introducing more complexity, not less.

For legacy code, there is no easy way out other than to split sections off and attempt to isolate them. Then take the time required to understand the rest of the system. There's no silver bullet here, you will just have to get your hands dirty.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.