CODE QUALITY
Nov 19, 2024

How to Refactor Duplicate Code with examples

Amartya | CodeAnt AI Code Review Platform

Amartya Jha

Founder & CEO, CodeAnt AI

How to Refactor Duplicate Code with examples
How to Refactor Duplicate Code with examples
How to Refactor Duplicate Code with examples

Table of Contents

Duplicate code is one of those problems every developer thinks they will “fix later” until it spreads everywhere. A few copied constants here, a repeated function there, and suddenly your codebase is harder to maintain, slower to debug, and full of hidden risks. Code duplication is not just untidy, it is a silent cost that grows with every commit.

In this guide, we break down the major types of code duplication: data, type, and algorithm, with real examples. More importantly, we show how to refactor duplicate code into cleaner, safer, and more scalable solutions that save time and prevent bugs before they start.

Types of Code Duplication

Code duplication quietly creeps into every codebase, making systems harder to maintain and easier to break. Understanding the different forms it takes is the first step toward cleaner, more reliable software. Let’s look at the main types of code duplication developers encounter.

  1. Data Duplication

Data Duplication occurs when the same data or constants are repeated in multiple locations within a codebase. This type of duplication is often straightforward to identify and can lead to several issues, including:

  • Inconsistency: If the duplicated data needs to be updated, forgetting to change it in every location can lead to discrepancies and bugs.

  • Maintenance Overhead: Maintaining multiple copies of the same data increases the workload for developers, particularly when changes are required.

Example of Data Duplication Consider a scenario where a constant value, such as a tax rate, is hard-coded in multiple places:

def calculate_tax(amount):
    return amount * 0.07  # Tax rate is duplicated

def calculate_total(price):
    tax = calculate_tax(price)
    return price + tax

To eliminate this duplication, you can define the tax rate as a constant:

TAX_RATE = 0.07

def calculate_tax(amount):
    return amount * TAX_RATE

def calculate_total(price):
    tax = calculate_tax(price)
    return price + tax

This refactoring reduces redundancy and simplifies future updates.

  1. Type Duplication

Type Duplication arises when similar methods operate on different data types but perform essentially the same logic. This type of duplication can be more challenging to detect since it often involves different classes or structures that share similar behaviors.

Example of Type Duplication Imagine two methods that process different types of user data:

public void processUser(User user) {
    // Process user
}

public void processAdmin(Admin admin) {
    // Process admin
}

These methods can be refactored using generics or interfaces to eliminate type duplication:

public void processUser(User user) {
    // Process user
}

public <T extends User> void processUserType(T user) {
    // Process either User or Admin
}

By using generics or polymorphism, you can create a single method that handles both types, reducing code duplication and enhancing maintainability.

  1. Algorithm Duplication

Algorithm Duplication occurs when similar algorithms or logic are repeated in different parts of the codebase. This can lead to inefficiencies, as any changes to the algorithm must be replicated across all instances where it appears.

Example of Algorithm Duplication Consider two functions that perform similar calculations but with slight variations:

function calculateDiscountedPrice(price) {
    return price * 0.9; // 10% discount
}

function calculateSeasonalDiscountedPrice(price) {
    return price * 0.85; // 15% discount
}

You can refactor these functions into a single method that accepts parameters for flexibility:

function calculateDiscountedPrice(price, discountRate) {
    return price * (1 - discountRate);
}

const regularPrice = calculateDiscountedPrice(100, 0.1);
const seasonalPrice = calculateDiscountedPrice(100, 0.15);

This approach not only reduces duplication but also makes it easier to introduce new discount strategies without duplicating logic.

Strategies for Refactoring Duplicate Code

Duplicate code doesn’t have to stay in your codebase. With the right techniques, you can refactor it into cleaner, reusable components that scale more easily. Here are some proven strategies developers use to eliminate duplication and simplify maintenance.

1. Extract Method

Identify repetitive code blocks and move them into a separate reusable method. This is particularly useful for shared logic across different parts of your application.

Before Refactoring:

public void calculate() {
    int sum = 0;
    for (int i = 0; i < numbers.length; i++) {
        sum += numbers[i];
    }
    System.out.println("Sum is " + sum);
}

public void displayAverage() {
    int sum = 0;
    for (int i = 0; i < numbers.length; i++) {
        sum += numbers[i];
    }
    System.out.println("Average is " + (sum / numbers.length));
}

After Refactoring:

private int calculateSum() {
    int sum = 0;
    for (int number : numbers) {
        sum += number;
    }
    return sum;
}

public void calculate() {
    System.out.println("Sum is " + calculateSum());
}

public void displayAverage() {
    System.out.println("Average is " + (calculateSum() / numbers.length));
}

This reduces redundancy and makes the code easier to update and debug.

2. Introduce Abstraction

Encapsulate shared functionality into a higher-level abstraction, such as a superclass, interface, or utility class. Example: If multiple classes share common methods like startEngine or stopEngine, create a base class:

public abstract class Vehicle {
    public abstract void startEngine();
    public abstract void stopEngine();
}

public class Car extends Vehicle {
    public void startEngine() { /* ... */ }
    public void stopEngine() { /* ... */ }
}

This promotes code reuse and simplifies future extensions.

3. Replace Conditional Logic with Polymorphism

If duplicate logic arises from repetitive conditional statements, use polymorphism to eliminate them.

Before Refactoring:

if (shape.equals("Circle")) {
    // Circle logic
} else if (shape.equals("Rectangle")) {
    // Rectangle logic
}

After Refactoring: Define an abstract Shape class with specific implementations for Circle and Rectangle.

4. Consolidate Duplicated Classes or Methods

Merge similar methods or classes by parameterizing differences. Example: Consolidate methods that vary slightly by accepting parameters or flags:

public void processUser(String userType) {
    if ("Admin".equals(userType)) {
        // Admin logic
    } else if ("Guest".equals(userType)) {
        // Guest logic
    }
}

This reduces duplication and centralizes behavior.

5. Utilize Libraries or Frameworks

Leverage existing libraries to reduce custom implementations of commonly used functionality. For example, use Java's Stream API to avoid manual iteration and summation in lists.

General Refactoring Tips for Cleaner Code

Bad code does not get fixed by accident. It gets fixed with discipline. Refactoring is where small, deliberate changes add up to big improvements in reliability and scalability. Below are the core practices that keep refactoring from being random cleanup and turn it into lasting impact.

  1. Apply the Single Responsibility Principle (SRP)

The Single Responsibility Principle (SRP), part of the SOLID principles of software design, states that every class or method should have one and only one reason to change. This means breaking down complex classes or methods into smaller, focused components, each handling one specific piece of functionality. Here's why and how:

  • Why SRP Matters:

    • Maintainability: Small, focused components are easier to update, debug, and test.

    • Reusability: Components designed with a single purpose can often be reused in different parts of the system.

    • Collaboration: Smaller responsibilities reduce the risk of conflicts among developers working on the same class or method.

  • How to Apply SRP:

    • Identify distinct responsibilities: For instance, in an e-commerce application, separate "Order Processing" (e.g., calculating totals) from "Logging" (e.g., saving records for audits).

    • Delegate responsibilities: Use helper classes or methods for specific tasks.

    • Modularize functionality: Break down large classes into smaller ones and large methods into smaller, purpose-specific methods.

Example Before Refactoring:

class Order {
    public void processOrder() {
        // Process order
        // Log order details
    }
}

Example After Refactoring:

class OrderProcessor {
    public void processOrder() {
        // Process order
    }
}

class OrderLogger {
    public void logOrderDetails() {
        // Log order details
    }
}
  1. Test Frequently

Testing is the backbone of successful refactoring, ensuring your changes do not introduce bugs or break existing functionality.

  • Write or Update Unit Tests Before Refactoring:

    • Use a test-first approach to define the desired behavior of the code.

    • Existing unit tests act as a safety net; refactor confidently knowing changes can be verified.

  • Benefits of Frequent Testing:

    • Immediate feedback: Catch errors as soon as they occur.

    • Reduced risk: Avoid introducing regressions while restructuring.

    • Validation of Correctness: Ensure new, refactored components meet the original functionality requirements.

    • Strategies for Testing During Refactoring:

      • Baseline Tests: Run all tests before refactoring to confirm the code's starting state is stable.

      • Incremental Testing: Test after each small change instead of waiting until the end.

      • Regression Testing: Ensure edge cases, and prior bugs fixed in the code, are still handled correctly.

  1. Use Refactoring Tools

Modern Integrated Development Environments (IDEs) like IntelliJ IDEA, Eclipse, or Visual Studio Code provide automated refactoring tools to make the process smoother and less error-prone.

  • Benefits of Refactoring Tools:

    • Accuracy: Tools handle re-naming, extracting, and formatting without introducing human errors.

    • Time-saving: Automation speeds up repetitive tasks.

    • Confidence: Refactoring tools usually integrate with the IDE's error-checking, minimizing oversight.

  • Key Features in IDE Refactoring Tools:

    • Extract Method/Variable: Converts a small block of code into a method or extracts an expression into a reusable variable.

    • Inline Method/Variable: Merges a method or variable directly into its callers for simplicity.

    • Rename: Safely renames classes, methods, or variables, updating all references across the codebase.

    • Move Class/Method: Reorganizes classes or methods into more appropriate locations.

    • Refactor Preview: Shows a preview of changes before applying them, allowing you to confirm.

Example Using Refactoring Tools: If you're working on a long method, you can:

  1. Highlight the repetitive code block

  2. Use "Extract Method"

  3. Rename the new method to something descriptive using "Rename."

Conclusion

  • Work Incrementally: Refactor in small, manageable chunks rather than attempting sweeping changes. This minimizes risks and makes it easier to track issues.

  • Follow Coding Standards: Refactoring is a good opportunity to clean up code formatting, naming conventions, and adhere to team guidelines.

  • Communicate with the Team: Share your refactoring goals and changes with team members, ensuring everyone understands the new structure and rationale.

By following these principles and leveraging the power of testing and refactoring tools, developers can ensure their codebase remains clean, robust, and adaptable to future needs.

FAQs

How do you refactor duplicate code?

How do you refactor duplicate code?

How do you refactor duplicate code?

What are the main types of code duplication?

What are the main types of code duplication?

What are the main types of code duplication?

How do you identify duplicate code?

How do you identify duplicate code?

How do you identify duplicate code?

Why is duplicate code bad?

Why is duplicate code bad?

Why is duplicate code bad?

What causes code duplication?

What causes code duplication?

What causes code duplication?

Unlock 14 Days of AI Code Health

Put AI code reviews, security, and quality dashboards to work, no credit card required.

Share blog:

Ship clean & secure code faster

Avoid 5 different tools. Get one unified AI platform for code reviews, quality, and security.

Ship clean & secure code faster

Avoid 5 different tools. Get one unified AI platform for code reviews, quality, and security.

Ship clean & secure code faster

Avoid 5 different tools. Get one unified AI platform for code reviews, quality, and security.