C++ – Dealing with Global Variables in Legacy Code

cdesigndesign-patternsobject-oriented

So… We have this fairly complex project (~10k LOC, but there's duplicated code so it's hard to tell) with hundreds of global variables. The project has more dependencies on other projects, and many other projects depend on it too. I have mostly inherited the responsibility to refactor just a part of this project by myself, an "enclosed section" of modules. None of the original developers remain.

I have devised a way to structure the routines in classes (breaking up megamoths, forming some class hierarchies, a little Strategy here and there, nothing too fancy (I hope); the objective is to make it easier for other developers to add functionality and make it possible to add proper unit tests).

My new classes provide calculations applied to currently-global arrays of data that are updated, and the calculations themselves need to maintain a state (a sum, the last value of the last processed vector, etc). You could see them as functors.

I'm unsure on how to deal with globals, though. I don't think I will be able change all globals to non-global because of the dependencies to other modules, which I'm not going to refactor just now. Also, many of my new classes will need to share data. So, I can…

  • For the globals that are not shared between my new classes, leave them as they are. I was thinking of using a Registry, or at the very least use some #define's or other variables to limit scope and provide context wherever they're used.
  • For the globals that will be shared between my new classes, either make a base class with the references, or use a Singleton to pass them around, or a combination of both.

I'm not sure if using these patterns will do more harm than good.

So, my question is: what's better, live with existing global variables or aggressively tune them with patterns like Singleton or Registry? Do you have any suggestions to these schemes, or have a better scheme?

Best Answer

One way to slowly and relatively safely move from globals to no-globals might be to group globals into semi-reasonable classes as best you can. Declare a global instance of the new class, with members that correspond to the globals you are replacing. Remove the globals. Wade through possibly massive numbers of compiler errors, changing each reference to the global to a reference to the corresponding member of the global instance.

Now you can gradually switch from having your classes know about the global instance of your new class directly, and instead kick the can down the road by having the global instance passed into them. Over time you can hope to kick the can all the way to the end of the road, by removing all direct knowledge of the global instance from every class, and finally getting rid of the global instance, having effectively slowly replaced it by a registry or dependency injection (depending on what you did each time you removed direct knowledge of the global instance from one of your classes).

Note that every time one of your classes loses direct knowledge of how to find the global instance it becomes more modifiable and testable, even if the global instance still exists out there in your code base. So you get some benefits without having to do all the work up front, which could take too long with a large code base if the global(s) are widely referenced.

Of course, you do have to finish doing everything in the first paragraph. With a large code base you and your compiler could get to spend a lot of quality time together.

Related Topic