How to refactor global variables out of your code

Tag: refactoring , global Author: suqingdi Date: 2009-08-19

The discussion around global variables and their misuse seems to hold a certain dogmatic tone to it. I am not here to dispute the "globals are bad" concept as it makes sense to me why they are bad. However I was wondering if people had some interesting code snippets that demonstrate exactly how to effectively refactor higher scoped variables and objects out of the code. In this question I am looking for examples or patterns of generic but useful solutions to the "I need to use a global variable here because it is easy" problem.

Here is a hypothetical and perhaps contrived example. I am using the global variable to keep track of the parameters sent to a function. And then if there is a failure that happens further down the chain I could go back and call the function again using parameters from the global variable.

public var myGlobalState:Object = new Object();

public function addPerson (name:String, person:Object, personCount:int, retryCount:int):void
{
     myGlobalState = null; // Clear out old values

     myGlobalState = new Object();
     myGlobalState.name = name;
     myGlobalState.person = person;
     myGlobalState.personCount = personCount;
     myGlobalState.retryCount = retryCount;

     person.userId = personCount + 1;
     person.name = name;

     savePerson(person);
}

public function savePerson (person:Object):void
{
    // Some code that attempts to save the person object properties to a database...
    // The process returns a status code for SUCCESS of FAILURE.

    // CODE TO SAVE TO DATABASE ....

    // Return status code...

    if (status == "fail")
    {
        // Retry at least once by calling the addPerson function again

        if (myGlobalState.retryCount < 3)
        {
            addPerson (myGlobalState.name, person, myGlobalState.personCount, myGlobalState.retryCount);
        }
    }
}

Best Answer

I don't have a snippet, but I have a real world example. Linear calibration constants (mass spectrometry field) in an application were global and there were complicated code to store and restore the global calibration constants for different spectra. Usage of the two values were spread all over the program, and it was difficult to change or check that conversion between uncalibrated and calibrated mass values using the two constants were correct in all cases.

I refactored by encapsulating the two calibration constants in a class that had the responsibilty of converting between uncalibrated and calibrated mass values. Functions to do the conversion were also introduced so it was centralised in one place in the program instead of being spread all over the program. This encapsulation later made it easy to introduce a new kind of calibration (not linear).

Instead of accesssing the two global variables the class that represented a spectrum would instead have and use an instance of the new calibration class, each instance with its own set of calibration constants.

Other Answer1

The answer usually lies in the architecture of your program. You could design in in a way global variables are absolutely necessary and you could design in in the way you never need them. You would usually end up with a better and cleaner architecture in the later scenario plus would avoid all of the usual problems with creating unit tests for the methods which rely on the global variables etc.

This question will also help.

P.S. In your particular scenario the global variable is not really needed at all - you could easily pass it as a parameter to the addPerson method.

comments:

<blockquote>In your particular scenario the global variable is not really needed at all - you could easily pass it as a parameter to the addPerson method.</blockquote> That is the part I am a little confused on. Would it be an optional parameter? So would it be something like public function addPerson (name:String, person:Object, personCount:int, retryCount:int, callBackParams:Object = null):void where callBackParams behaves the same way as myGlobalState in my example?
That's one way of doing it

Other Answer2

A quick solution would be by adding all your global variables inside one huge object, possibly with several child objects to separate the groups of data. With all those variables in one object, all you would need is one global variable to store this object. All your code would then refer the variables in this object instead the global variables.

The next step would be getting rid of this single, global object. And that should be easier than getting rid of a few hundreds of global variables. This could be done by changing it into an additional parameter that you pass along to any other methods.

Once all global data is gone, you can think about refactoring your code, trying to optimize this shared object by e.g. dividing it into multiple smaller objects. But by moving everything inside a single object, you make it all easier to manage.