There’s a simple set of refactoring steps that I use often for this exact purpose. First let’s describe the problem with an example.
Let’s say you’re developing a command-line Node.js utility (as I currently am while I write this post). As you start building your app, you’ll find yourself adding a variety of functions for different purposes and throwing them into a general-purpose file. I often call mine
As your general-purpose function file grows, often you’ll see a pattern: in this case, there are 3 methods that center around the purpose of managing a configuration object. In addition, you may be currently referring to those methods like this:
There’s another pattern here: 2 of these functions are referring to a single config object. Anytime you have a collection of functions that are working off a shared set of data, it’s justifiable to (1) put these methods into their own file and (2) change this object into an instantiatable class. The benefits are:
- Modularity. These functions are in their own category, and can be separated from the other general-purpose functions.
- Code Reusability. If you need to use the same functions with different sets of data, you can (by creating multiple class instances).
- Code Readability. Your function (method) names and their parameters can often be shortened.
- Separation of Concerns. If these functions are mixed in with others around different purposes, you can break them into a single-purpose class.
How to convert your object to a class
Here’s the overview of steps:
- Change the object itself
- Add class keyword to object definition
- Save any shared data as an instance property
- Change non-instance related methods to static
- Change references to the object
- Instantiate using the new keyword
- Static methods stay the same!
- Clean up and reap the benefits
Let’s get rolling!
Part one: converting your object definition
First we create a new file (
config.js) for our
Config class, and change the definition by adding the
Now we’re exporting a class that we can create multiple instances of. But for the existing functionality to continue working as we intend it do, we’ll have to make some other internal changes.
As we move our functions over to the new file, we take that shared data we’re passing into every function and save it to the instance. We load the config object however we were before, but save it to the instance using the
Now our config property will exist whenever I refer to this instance after calling
initConfig(). From here on in, I’ll be calling these functions “methods”, since they’re now part of a class.
We’re left with any methods that don’t operate on the shared data. These don’t need to get added to every instance of the class and bloat our application, so we can make them static methods and call them directly on the class.
Part two: changing existing references
First we make sure to create a class instance that we’ll operate off of. Let’s initialize our config instance, and change the call to initConfig(). Since this class is in its own file now, we’ll change the import statement too:
And now we change the reference to the static method to access it through the new
Config instance instead of the now-separate
Utils object. We can also remove the old
config argument, since we no longer have to reference this variable externally:
At this point the minimal amount of work is done: our code is changed into a class and the calls to it will still function. Now to go the extra mile and clean it up, for more readable and unambiguous code.
Part three: clean up and reap the benefits
To me this is one of the best parts of refactoring code (maybe because I’m a neat freak): making things clean and orderly.
We’ve already removed arguments to any functions that pass the config object around, since we’re referring to the shared object internally now. Now we can also remove the word
config from the method names, since it’s implicit in the class name. Shorter method names means better readability. The final code for both files looks like this:
And now we have a far cleaner and more reusable object class than we had before.
This pattern comes up for me a lot, and is helpful when you develop your apps progressively; it’s very useful for adding structure to your codebase as it grows.
Have you used this pattern before? Let me know in the comments.