What is this… refactoring?
Refactoring is often belittled to “cleaning up code.” However, according to Martin Fowler, it is much more than that. It is effectively cleaning code and creating a more stable, maintainable, and reliable internal structure while maintaining the existing external behavior.
So, why do we need to refactor our code?
In a perfect world, we would all write perfect code with 20/20 foresight and refactoring wouldn’t need to take place. However, in the real world, a system can grow increasingly unstable and bloated if features are quickly added or deadlines are given higher priority than code quality. As projects mature and features get added in, the original project design can get obscured due to changing requirements or rushed coding. When things begin to stray from the original design, it is a good idea to take a little bit of time to figure out where things have gone wrong.
Code that maintains a solid design is easier to understand and makes future development less complicated, since new features can more easily be added to a stable project. Projects that lose touch of their initial design can be difficult for future (or even current) developers to understand, leading to wasted time trying to comprehend what the code is doing.
That sounds great, but when will I have time to do that?
Refactoring should typically not be a necessary phase in the lifecycle of a project. It should be an ongoing process as features are added or modified. Fowler describes what he calls “bad smells” to help determine if a unit of code needs refactoring. These methods can be split into five distinct categories.
Bloaters are typically caused by long-term accumulations of poor coding choices. Resolving these will make classes and functions more manageable. They can be detected by the following code smells.
- Long Method
A method seems excessively long, and can likely be split into shorter, more meaningful methods.
- Large Class
A class seems to contain too much functionality, and can likely be split into smaller, more descriptive classes.
- Primitive Obsession
Primitives tend to be used more often than being grouped together into meaningful classes.
- Long Parameter List
A method takes many parameters.
- Data Clumps
Data tends to be grouped together in portions of the code, and can often be extracted as its own class.
Object-Orientation abusers show up due to improper implementation of OOP principles. By reducing these, the code’s quality can be increased and be more easily understood by future developers.
- Temporary Field
A class contains fields that only need to be set in certain circumstances, making the code more difficult to understand.
- Refused Bequest
A subclass inherits the methods and data from the parent class, but needs very little of what is actually handed down.
- Alternative Classes with Different Interfaces
Two classes perform identical functions but have different method names.
Change preventers do exactly what their name implies: they prevent efficient changes to code. By fixing these, quality code can be produced more efficiently.
- Divergent Change
A class is modified in many different ways for many different reasons, which typically means that multiple classes can be used that are only modified for a particular reason.
- Shotgun Surgery
Many small changes are needed in multiple different classes when a type of change is made.
- Parallel Inheritance Hierarchies
In order to create the subclass of one class, you must create a subclass of another.
Dispensables are chunks of code that nobody needs around. Removing them makes the code more efficient and readable.
When there are large sections of comments, it can often mean that the code is poorly written and the comments are there to make sense of it all.
- Duplicate Code
A code structure is repeated in multiple places throughout a program.
- Lazy Class
A class doesn’t seem to be doing enough to entail it to be a standalone class.
- Data Class
Data classes contain only fields, getters, and setters, but no methods.
- Speculative Generality
A class or method is generalized to handle much more than it is required to.
- Dead Code
A class or method is no longer needed but it was not removed.
These smells are all caused by classes being tightly coupled. By resolving these smells, the code becomes more organized and easier to understand.
- Feature Envy
A method uses more features from a class other than the one it is a member of.
- Message Chains
A class asks an object for another object, which in turn asks another object for an object, etc.
- Middle Man
A class delegates tasks to other classes instead of performing the task itself.
- Inappropriate Intimacy
When two classes share too much information, they can often be modified so that the pieces that share information can be extracted to form a unique class.
- Incomplete Library Class
A library class, or class that contains many useful but not necessarily related methods, does not contain expected methods.
In this post we talked about the code smells that reveal where we might need to consider refactoring our code. In the next post on refactoring, we will determine how to resolve some of these smells.
- Refactoring: Improving the Design of Existing Code – Martin Fowler