This content originally appeared on DEV Community and was authored by Tome Karkalashev
1. What is Refactoring?
1.1. Concept of refactoring
Code refactoring is the process of restructuring existing code without changing its external behavior / functions.
1.2. What is Clean Code?
Clean code is a code that is easy to read/understand, like a book where each word transforms into a picture and you can actually visualize everything like watching a movie, in short, should be readable by Humans.
1.3. Why Refactoring?
To Keep Your Code Clean (easier to modify and understand).
To Improve The Performance of The Application.
To Save Time and Money in The Future.
To Make Bugs Easier to Be Found.
To Improves the System Design.
To spread knowledge and To improve teamwork ability.
1.4. Refactoring Principles
Use incremental steps. Change a bit of code, then check if everything works.
Keep refactorings small. The bigger a change, the riskier it is.
Don't start adding functionality in the middle of a refactoring.
When it leads you nowhere, abandon the whole thing.
Reduce code but avoid clever code.
Only refactor code that has tests.
2. Refactoring Techniques
2.1. Commenting
2.1.1. How to Comment Meaningfully?
Top-level: A top-level comment should give the reader an initial explanation of the purpose and responsibilities of a file.
Class-level: In order to understand the need for a certain class, a developer should add a short description to each class they define.
Function-level: function-level commenting focuses on making the purpose of a function clear.
2.2. Composing Methods
2.2.1. Extract Method
- Problem: You have a code fragment that can be grouped together.
printOwing(): void {
printBanner();
// Print details.
console.log("name: " + name);
console.log("amount: " + getOutstanding());
}
- Solution: Move this code to a separate new method (or function) and replace the old code with a call to the method.
printOwing(): void {
printBanner();
printDetails(getOutstanding());
}
printDetails(outstanding: number): void {
console.log("name: " + name);
console.log("amount: " + outstanding);
}
2.2.2. Inline Method
- Problem: When a method body is more obvious than the method itself, use this technique.
class PizzaDelivery {
// ...
getRating(): number {
return moreThanFiveLateDeliveries() ? 2 : 1;
}
moreThanFiveLateDeliveries(): boolean {
return numberOfLateDeliveries > 5;
}
}
- Solution: Replace calls to the method with the method’s content and delete the method itself.
class PizzaDelivery {
// ...
getRating(): number {
return numberOfLateDeliveries > 5 ? 2 : 1;
}
}
2.2.3. Extract Variable
Problem: You have an expression that’s hard to understand.
Solution: Place the result of the expression or its parts in separate variables that are self-explanatory.
2.2.4. Inline Temp
Problem: You have a temporary variable that’s assigned the result of a simple expression and nothing more.
Solution: Replace the references to the variable with the expression itself.
2.2.5. Replace Temp with Query
Problem: You place the result of an expression in a local variable for later use in your code.
Solution: Move the entire expression to a separate method and return the result from it. Query the method instead of using a variable. Incorporate the new method in other methods, if necessary.
2.3. Moving Features between Objects
2.3.1. Move Method, Properties
Problem: A method is used more in another class than in its own class.
Solution: Create a new method in the class that uses the method the most, then move code from the old method to there. Turn the code of the original method into a reference to the new method in the other class or else remove it entirely.
2.3.2. Extract Class
Problem: When one class does the work of two, awkwardness results.
Solution: Instead, create a new class and place the fields and methods responsible for the relevant functionality in it.
2.4. Organizing Data
2.4.1. Self Encapsulate Field
- Problem: You use direct access to private fields inside a class.
class Range {
private low: number
private high: number;
includes(arg: number): boolean {
return arg >= low && arg <= high;
}
}
- Solution: Create a getter and setter for the field, and use only them for accessing the field.
class Range {
private low: number
private high: number;
includes(arg: number): boolean {
return arg >= getLow() && arg <= getHigh();
}
getLow(): number {
return low;
}
getHigh(): number {
return high;
}
}
2.4.2. Replace Data Value with Object
Problem: A class (or group of classes) contains a data field. The field has its own behavior and associated data.
Solution: Create a new class, place the old field and its behavior in the class, and store the object of the class in the original class.
2.5. Simplifying Conditional Expressions
2.5.1. Consolidate Duplicate Conditional Fragments
- Problem: Identical code can be found in all branches of a conditional.
if (isSpecialDeal()) {
total = price * 0.95;
send();
} else {
total = price * 0.98;
send();
}
- Solution: Move the code outside of the conditional.
if (isSpecialDeal()) {
total = price * 0.95;
} else {
total = price * 0.98;
}
send();
2.5.2. Replace Conditional with Polymorphism
- Problem: Identical code can be found in all branches of a conditional.
class Bird {
// ...
getSpeed(): number {
switch (type) {
case EUROPEAN:
return getBaseSpeed();
case AFRICAN:
return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
case NORWEGIAN_BLUE:
return (isNailed) ? 0 : getBaseSpeed(voltage);
}
throw new Error("Should be unreachable");
}
}
- Solution: Move the code outside of the conditional.
abstract class Bird {
// ...
abstract getSpeed(): number;
}
class European extends Bird {
getSpeed(): number {
return getBaseSpeed();
}
}
class African extends Bird {
getSpeed(): number {
return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
}
}
class NorwegianBlue extends Bird {
getSpeed(): number {
return (isNailed) ? 0 : getBaseSpeed(voltage);
}
}
// Somewhere in client code
let speed = bird.getSpeed();
2.6. Simplifying Method Calls
2.6.1. Rename Method
Problem: The name of a method doesn’t explain what the method does.
Solution: Rename the method.
2.6.2. Hide Method
Problem: A method isn’t used by other classes or is used only inside its own class hierarchy.
Solution: Make the method private or protected.
2.6.3. Preserve Whole Object
- Problem: You get several values from an object and then pass them as parameters to a method.
let low = daysTempRange.getLow();
let high = daysTempRange.getHigh();
let withinPlan = plan.withinRange(low, high);
- Solution: Instead, try passing the whole object.
let withinPlan = plan.withinRange(daysTempRange);
Thanks for reading!
This content originally appeared on DEV Community and was authored by Tome Karkalashev
Tome Karkalashev | Sciencx (2022-07-09T05:06:11+00:00) Refactoring. Retrieved from https://www.scien.cx/2022/07/09/refactoring/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.