Refactoring
lastmod 2018-07-13created 2015-03-172 edits
In software engineering, refactoring mainly means 'restructuring code without changing its results'. It mainly improves readability and makes maintenance easier. It is not the act of removing bugs or adding new features. It is a maintenance activity that changes and improves internal logic or structure while leaving the external screens users see unchanged.
Martin Fowler's book Refactoring catalogs a variety of refactoring patterns. A few representative ones are Encapsulate Field, Extract Method, Generalize Type, and Rename Method.
Why refactoring is needed
- Improves the design of software.
- Makes code easier to understand.
- Makes it easier to find bugs in code.
- Speeds up programming and increases productivity.
When to refactor
Do it when you find yourself repeating the same task for the third time. Refactor when it is hard to add a feature.
Getting started with refactoring (feat. code smells)
Find the 'smelly' parts of your code and fix them.
- Duplicated Code
- For duplicated code within one class, extract the duplicated part into a single method. When duplicated code exists in two unrelated classes, extract it into a third class (or module) through Extract Class or Extract Module.
- Long Method
- Boldly break methods into small units. Instead of adding a comment where one seems needed, write a method and put the commented code inside it. Name short methods well so they are easy to understand. Name a method after its purpose, i.e., the function itself, not how it performs the function.
- Large Class
- A class with too many features contains a huge number of instance variables. If they share a prefix or suffix, it is good to extract them into one class. For a class that doesn't use all of its instance variables all the time, apply Extract Class or Extract Module.
- Long Parameter List
- As parameters multiply, they become inconsistent with each other or inconvenient to use, and since they must keep being changed whenever more data is needed, the parameters become hard to understand. In this case, pass the whole object, or replace parameters with methods.
- Divergent Change
- Occurs when one class is frequently changed in different ways for different reasons. In this case, it is good to split the class into several variant objects. Extract the parts that change for a particular reason into a separate class.
- Shotgun Surgery
- Suspect this if every change requires fixing many parts across several classes. In this case, extract the methods or fields into one class. If none of the existing classes is suitable, create a new class and extract them into it.
- Feature Envy
- The essence of objects is the technique of bundling data together with the processes that use that data. If you often call getter methods to access another object's data, suspect this. In this case too, move the method to the appropriate class.
- Data Clumps
- Data items often form groups with other data. Such clustered data should be turned into objects. Find the places where the data clumps look like fields and extract them into a separate class. Parameters then naturally get passed as whole objects.
- Primitive Obsession
- Convert data values into objects.
- Switch Statement
- Most switch statements should be replaced with overriding without a second thought. Build an inheritance structure and replace the conditional with overriding. If there are only 2–3 case statements and none of them will need changing, replacing them with overriding is overkill.
- Parallel Inheritance Hierachies
- Suspect this if every time you create a subclass of one class, you also have to create a subclass of another class. In this case, to remove the duplicated code, make instances of one inheritance hierarchy refer to instances of the other.
- Lazy Class
- A lazy class is a class whose functionality has shrunk due to refactoring, or a class written with plans for changes that were never made and so became useless. Such classes are not worth their cost, so remove them by merging them or inlining the class.
- Speculative Generality
- Code for features added because they might be needed soon, but not yet needed. You can also suspect this if a method or class is used only by test cases.
- Temporary Field
- Sometimes an instance variable in an object is set only in certain situations. Such parts should be extracted into a separate class. Also suspect this when a complex algorithm uses several variables. The field members used by the algorithm are not used otherwise, so they only increase complexity. Extract them into a separate class.
- Message Chains
- A message chain is a chain of requests in which asking one object for a second object leads the second object to ask for a third, and so on. In this case, find out what the resulting object is used for, extract it into a separate method, then apply Move Method.
- Middle Man
- If more than half of the methods in a class's interface delegate their functionality to another class, change it to access the object that actually implements the logic directly. You can then extend functionality without having to trace every delegation.
- Inappropriate Intimacy
- Classes that are too involved with each other should be separated, turning the bidirectional links into unidirectional ones. Or you can create a new separate class that acts as a middle man. Excessive intimacy often arises from inheritance, which is solved by replacing it with delegation.
- Alternative Classes with Different Interfaces
- Rename methods that do the same thing but have different signatures.
- Data Class
- A data class is a class that has only fields and getter/setter methods for them. Most data manipulation is done by other classes. For fields that must not change, restrict the set method with the private access modifier.
- Refused Bequest
- A subclass inherits the methods and data of its parent class. The parts the subclass doesn't need are left unused. This happens because of a wrong hierarchy, and you should use Push Down Method or Push Down Field so that no inherited parts are left unused.
- Comments
- Comments must not be used as a deodorant to hide code smells. When you feel the need to add a comment, first try refactoring the code so the comment becomes unnecessary.