“Refactoring” is a term for changing the structure of code without changing its behavior. This is often necessary because since it’s hard enough getting code to do what we want without worrying about how nicely it is structured it’s easy to end up with code that is functional and but also kind of a mess.

Refactoring can be as small as changing the name of a variable to something more accurate or descriptive or as big as a complete reorganization of a whole program.. Below I describe some refactoring patterns that we’ll use in this class.

Renaming a variable

The names of variables (including function names) are purely for the benefit of human readers of your code, including that most important human of all: you. It is always worth changing a variable name to be more descriptive of its true purpose, especially because it’s so easy.

The recipe

  1. Use the Rename symbol function in the editor. Either right click on the variable name and choose “Rename symbol” from the pop-up menu or hit F2 when the cursor is in the variable name and then type the new name. The editor is smart enough to only change the places where the old name is actually referring to the same variable—it won’t change, for instance, variables with the same name in other functions.

Extracting a function

One of the simplest but most powerful refactoring patterns is to extract a function. Sometimes we extract a function because the same code is repeated multiple places and we want to remove the duplication. But it’s also worth extracting functions because smaller functions are easier to understand and to make sure we’ve gotten them right. It’s almost always better to have one two-line function that calls two different ten-line functions than a single twenty-line function that does the same thing. So if you have a twenty-line function you should look for ways to extract some smaller functions using this refactoring.

The recipe

  1. Copy the lines of code you want to extract and paste them outside of the function they are currently in and at the top level of the file. (“Top level” means not nested inside any other function; if you use “Format document” in the editor to reformat your code the const and the closing } should be in the leftmost column of the file.)
  2. Wrap the code you just pasted in a function that takes no arguments. I.e. before the code define a function with const functionName = () => { and after it, put a closing }. Often you will already have a good idea what to call the new function, but if you don’t it’s fine to use a placeholder name to start with. Sometimes with particularly hairy code, you’ll only figure out what it’s actually doing after you’ve finished extracting it.
  3. Find the arguments to the function by examining the variables referenced in the function’s code. Any variable that is not a global variable and is referenced in the function before it is assigned a value needs to be made into an argument to the function.
  4. Find the return value by finding a variable that is a) not global b) not declared in the new function, and c) assigned a value and not used in the function after it is assigned to. Usually there will just be one of these and most likely it will be assigned at the end of the new function. Instead of assignin the value to the variable, return it. (If there are multiple such variables you maybe grabbed to much code for one function and need to go back and try again with less code.) Not all functions will have a return value—if the code is just for side effects such as drawing on the screen there may be no need to return anything.
  5. Find undeclared local variables in the function by finding variables that are not global and have not already been declared as either arguments to the fuction or as local variables with const or let. The need to be declared which just means you need to add a const or let in front of the variable name the first time it is assigned. Prefer const but use let if the variable is assigned to more than once in the new function.
  6. Replace the original code with a call to your new function, passing all the necessary arguments. If you converted an assignment to a a variable in the extracted function to a return then you should replace the same assignment in the original code to assigning the value of calling the function. If the code was duplicated, replace all the copies with calls to this function.
  7. Generalizing a function

    Anytime the code of a function contains a literal value (e.g. number like 10), you should ask whether the function would make sense if the value was replaced with a different value. I.e. would changing the value change the bahivor of the function without completely breaking it?

    If the answer is yes, it may be a good idea to replace the literal value with a variable and add that variable as an argument to the function.

    The recipe

    1. Find a literal value that occurs in the function’s code whose particular value is not an intrinsic part of the logic of the function.
    2. Add a new variable to the function’s argument list and name it meaningfully for the purpose the literal value is serving.
    3. Replace the literal with the variable everywhere it occurs. Note, however, that it is possible that the same literal value is being used for two different purposes within the function—if you can imagine changing one occurrence of the value without changing the other, then they are actually only coincidentally the same and you should not replace them both with the same variable.
    4. Add the literal value to all calls to the function as the value of the new argument.

    Extracting and generalizing a function

    Extracting a function, as described above, works to remove duplication if you have two exact copies of the same code—after extracting one copy into a new function you can use that function to replace all the occurrences of that code.

    Sometimes, however, you have almost duplicated code—code that is basically the same but with maybe different values in a few places. When that’s the case, we want to combine extracting a function with generalizing it in order to extract a function that can handle all of those instances of almost duplicated code.

    The recipe

    1. Extract one copy of the almost duplicated code according to the recipe for extracting a function. This will leave you with a function that fully replaces the exact code you extracted.
    2. Identify the differences between the extracted code and other nearly identical chunks of code.
    3. Genalize the function using the recipe above to replace the literal values in the extracted code with arguments to the function and adding the value as an argument where you call the function.
    4. Replace the other chunks of code with calls to the extracted function, passing the appropriate values for the added arguments.