Master Code Refactoring: Principles, Smells, and Practical Techniques
This article explains what refactoring is, why and when to apply it, lists common code smells with concrete examples, and provides step‑by‑step techniques for reorganizing functions, moving responsibilities, restructuring data, simplifying conditionals, and improving overall software design.
Refactoring Principles
Refactoring is an adjustment of a software’s internal structure that does not change its observable behavior, aiming to improve understandability and reduce the cost of future modifications.
Why Refactor
Improve software design: prevents gradual design decay.
Help locate bugs: deeper understanding makes bugs easier to find.
Increase development speed: better design and readability reduce errors.
When to Refactor
Refactoring should be done continuously, not as a separate scheduled task. The "three‑rule" suggests: the first time you do something, just do it; the second time you feel resistance; the third time you must refactor.
Common Code Smells
1. Duplicate Code
If the same code structure appears in more than one place, merge it into a single abstraction (method, superclass, or separate class).
2. Long Class
Large classes should be broken into smaller, cohesive functions; each function should be short and focused.
3. Large Class
Classes that try to do too many things accumulate many instance variables and become hard to maintain.
4. Long Parameter List
Too many parameters make a method hard to understand; consider passing an object instead.
5. Divergent Change
If a class frequently changes for many unrelated reasons, split it so each class changes for only one reason.
6. Shotgun Surgery
When a single change requires edits in many classes, the code is scattered and hard to maintain.
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
Replace switch statements with polymorphism when possible.
11. Parallel Inheritance Hierarchies
When adding a subclass forces you to add a subclass in another hierarchy, consider composition.
12. God Class
A class that has grown too large and does too many jobs should be split or removed.
13. Over‑engineered Future‑Proofing
Avoid adding hooks or special cases that are not needed now; they only add complexity.
14. Temporary Fields
Fields used only for a specific case make the code confusing; remove them when the case disappears.
15. Inconsistent Naming
When similar concepts have different names, unify the naming to avoid confusion.
16. Excessive Comments
Comments often indicate bad code; aim for self‑explanatory code and eliminate unnecessary comments.
17. Over‑coupled Message Chains
Long chains of method calls tightly couple client code to object navigation; simplify or introduce higher‑level methods.
18. Middle Man
When a class merely forwards calls to another, remove the middle man and let clients call the target directly.
19. Redundant Classes
If a class no longer has a distinct responsibility after refactoring, delete it.
20. Magic Numbers
Replace literal numbers with well‑named constants.
Reorganizing Functions
Extract Method
When a function is too long or needs a comment to explain its purpose, create a new method with a descriptive name and replace the original code with a call to the new method.
Inline Method
If 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 assigned once and used only in a simple expression with the expression itself.
Replace Temp with Query
Turn a temporary variable that stores the result of an expression into a method that returns that expression.
Introduce Explaining Variable
Assign a complex expression to a well‑named temporary variable to clarify its purpose.
Split Temporary Variable
If a temporary variable is assigned multiple times, create separate variables for each distinct purpose.
Remove Assignments to Parameters
Copy a parameter to a local variable before modifying it, preserving the original argument.
Replace Algorithm
Swap a confusing algorithm with a clearer one by rewriting the method body.
Moving Features Between Objects
Move Method
If a method uses another class more than its own, move it to the class it uses most.
Move Field
When a field is accessed more often by another class, relocate the field to that class.
Extract Class
Split a class that does two unrelated responsibilities into two classes.
Inline Class
If a class does too little to justify its existence, merge its behavior into another class and delete it.
Hide Delegate
Encapsulate a delegation relationship behind a higher‑level interface.
Remove Middle Man
Eliminate unnecessary delegation by letting clients call the target directly.
Introduce Additional Function
Add a helper function in a client class when you cannot modify the service class.
Introduce Local Extension
Create a subclass or wrapper to add new behavior without changing the original class.
Reorganizing Data
Encapsulate Field
Make a field private and provide getter/setter methods.
Replace Data Value with Object
Wrap related primitive data in a dedicated object that also provides behavior.
Replace Value Object with Reference
Share identical value objects by using a single reference.
Replace Reference with Value
Convert a small immutable reference object into a value object.
Replace Array with Object
Encapsulate array elements as fields of a new object.
Copy Observed Data
Synchronize GUI data with domain objects using the Observer pattern.
Bidirectional Association
Add a reverse reference when two classes need to know each other.
Unidirectional Association
Remove unnecessary reverse references.
Replace Magic Number with Constant
Introduce a named constant for any literal with special meaning.
Encapsulate Collection
Return an unmodifiable view of a collection and provide add/remove methods.
Simplifying Conditional Expressions
Decompose Conditional
Extract each part of a complex if‑then‑else into separate methods.
Consolidate Conditional Expression
Combine multiple tests that lead to the same result into a single condition.
Extract Method from Repeated Conditional Code
Move identical code from each branch outside the conditional.
Replace Control Flag with Break/Return
Eliminate boolean flags that control flow by using early exit statements.
Replace Conditional with Polymorphism
Turn type‑based conditionals into abstract methods overridden by subclasses.
Simplifying Function Calls
Rename Method
Give a method a name that clearly expresses its intent.
Add Parameter
Introduce a parameter object when a method needs additional information.
Remove Parameter
Delete parameters that are no longer used.
Separate Query from Modifier
Split a method that both returns a value and changes state into two distinct methods.
Parameter Object
Group frequently co‑occurring parameters into a single object.
Remove Setter
If a field is set only once at construction, eliminate its setter.
Make Method Private
Change methods that are never called from outside the class to private.
Replace Constructor with Factory Method
Encapsulate object creation logic in a static factory method.
Handling Generalizations
Pull Up Field
Move a field that appears in multiple subclasses to their common superclass.
Pull Up Method
Move identical methods from subclasses to the superclass.
Push Down Method
Move a method that is used only by certain subclasses down to those subclasses.
Extract Interface
Define a new interface for a common subset of methods used by several clients.
Collapse Hierarchy
Merge a superclass and subclass when they provide no additional behavior.
Replace Inheritance with Delegation
Prefer composition over inheritance when a subclass only forwards calls.
Replace Delegation with Inheritance
Use inheritance when a class contains many trivial delegating methods.
double basePrice = quantity * timePrice;
if (basePrice > 1000) {
return basePrice * 0.95;
} else {
return basePrice * 0.98;
}After extracting the price calculation:
if (basePrice() > 1000) {
return basePrice() * 0.95;
} else {
return basePrice() * 0.98;
}
double basePrice() {
return quantity * timePrice;
} final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") > -1;
final boolean wasResized = resize > 0;
if (isMacOs && isIEBrowser && wasInitialized() && wasResized) {
// do something
}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.
