Mastering Code Refactoring: Principles, Smells, and Practical Techniques
Refactoring transforms messy code into clean, maintainable structures without altering observable behavior, covering principles, common code smells, and step‑by‑step techniques such as extracting functions, moving methods, simplifying conditionals, and reorganizing data, empowering developers to improve design, reduce bugs, and accelerate development.
Refactoring Principles
Refactoring is an adjustment of a software’s internal structure that improves understandability and reduces modification cost while preserving its observable behavior.
It helps improve design, locate bugs more easily, and increase development speed.
When to Refactor
Refactoring should be done continuously rather than as a dedicated time‑boxed activity. The "Three‑Rule" suggests refactoring after the first, second, and third similar attempts at a task.
Typical triggers include adding new features, difficulty extending the design, or encountering bugs during review.
Common Code Smells
1. Duplicate Code
When identical code appears in multiple places, consolidate it into a single method, superclass, or utility class.
2. Long Class
Classes with many responsibilities should be broken into smaller, cohesive classes.
3. Large Class
Excessive fields and methods indicate a class is doing too much; split responsibilities.
4. Long Parameter List
Too many parameters make a method hard to use; consider passing an object instead.
5. Divergent Change
If a class changes for many unrelated reasons, separate the concerns into different classes.
6. Shotgun Surgery
When a single change requires modifications in many places, group related behavior together.
7. Feature Envy
A method that uses data from another class more than its own should be moved to that class.
8. Data Clumps
Groups of data that always appear together should be encapsulated in their own object.
9. Primitive Obsession
Replace primitive data with small value objects that carry behavior.
10. Switch Statements
Prefer polymorphism over large switch/case blocks.
11. Parallel Inheritance Hierarchies
When adding a subclass forces the creation of a parallel subclass, replace one hierarchy with a reference.
12. Redundant Class
Remove classes that have become empty after refactoring.
13. Speculative Generality
Delete unused hooks or abstractions that add unnecessary complexity.
14. Temporary Field
Fields used only in limited contexts should be replaced by local variables or queries.
15. Message Chains
Break long chains of method calls by introducing intermediate objects or methods.
16. Middle Man
Remove classes that merely delegate to another class; let clients call the target directly.
17. Data Class
Classes that only hold data should be turned into value objects or enriched with behavior.
18. Refused Bequest
When a subclass uses only a fraction of its superclass, consider extracting a new superclass.
19. Comments
Good code should be self‑explanatory; excessive comments indicate the need for clearer code.
20. Inappropriate Intimacy
Classes that know too much about each other should have their responsibilities separated.
Reorganizing Functions
Extract Method
Identify a long or unclear code block, create a new method with a descriptive name, and replace the block with a call to the new method.
double basePrice = quantity * timePrice;
if (basePrice > 1000) {
return basePrice * 0.95;
} else {
return basePrice * 0.98;
}After extraction:
if (basePrice() > 1000) {
return basePrice() * 0.95;
} else {
return basePrice() * 0.98;
}
private double basePrice() {
return quantity * timePrice;
}Inline Method
When a method’s body is as clear as its name, replace its calls with the method body and delete the method.
Inline Temporary Variable
Replace a temporary variable that is used only once with the expression itself.
Replace Temp with Query
Encapsulate a computed value in a method so all callers can reuse it.
Moving Features Between Objects
Move Method
If a method uses another class more than its own, create a similar method in the other class and delegate or delete the original.
Move Field
When a field is accessed more often from another class, relocate the field to that class and update all references.
Extract Class
Split a large class into two, moving related fields and methods to the new class.
Inline Class
If a class does too little, merge its responsibilities into another class and delete it.
Hide Delegation
Provide a façade that forwards calls, shielding clients from the delegation chain.
Remove Middle Man
Eliminate unnecessary delegating classes so clients call the target directly.
Introduce Auxiliary Function
Add a helper function in the client when you cannot modify the service class.
Introduce Local Extension
Create a subclass or wrapper that adds extra behavior to a service you cannot change.
Reorganizing Data
Encapsulate Field
Make fields private and provide getter/setter methods.
Replace Data with Object
Wrap related primitive data and behavior into a dedicated class.
Replace Array with Object
When array elements represent distinct concepts, model them as fields of a class.
Replace Magic Number with Constant
Introduce a named constant for any literal value that has meaning.
Encapsulate Collection
Return read‑only copies of collections and provide methods to modify the internal collection.
Simplifying Conditional Expressions
Decompose Conditional
Extract each branch of a complex if‑else into its own method.
Combine Conditions
Merge multiple tests that lead to the same result into a single expression.
Remove Control Flags
Replace boolean flags that control flow with early return or break statements.
Replace Conditional with Polymorphism
Turn a type‑based conditional into an abstract method overridden by subclasses.
Simplifying Function Calls
Rename Function
Give a method a name that clearly expresses its intent.
Add Parameter
When a method needs extra information, add a parameter that carries only the required data.
Remove Parameter
Delete parameters that are no longer used.
Separate Query from Command
Split methods that both return a value and modify state into distinct query and command methods.
Introduce Parameter Object
Group frequently co‑occurring parameters into a single object.
Handling Generalization Relationships
Pull Up Field / Method
Move identical fields or methods from subclasses to a common superclass.
Push Down Field / Method
Move a field or method from a superclass to the subclasses that actually use it.
Extract Subclass / Superclass
Create a new subclass for features used by only some instances, or a new superclass for shared features.
Extract Interface
Define an interface for the common subset of operations used by multiple clients.
Collapse Hierarchy
When a superclass and subclass are indistinguishable, merge them into a single class.
Template Method
Define the skeleton of an algorithm in a superclass and let subclasses provide the variable steps.
Replace Inheritance with Delegation (and vice‑versa)
Swap inheritance for composition when a subclass only uses a small part of the superclass, or replace many delegating methods with inheritance when appropriate.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
