Fundamentals 27 min read

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.

Java Backend Technology
Java Backend Technology
Java Backend Technology
Master Code Refactoring: Principles, Smells, and Practical Techniques

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
}
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

software designrefactoringclean codeObject-Orientedcode smells
Java Backend Technology
Written by

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!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.