Fundamentals 26 min read

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.

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

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.

Refactoring illustration
Refactoring illustration
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.

best practicessoftware designrefactoringclean codecode 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.