• Shift Elevate
  • Posts
  • Large Class: Extract Class Refactoring | Clean Code

Large Class: Extract Class Refactoring | Clean Code

Large Class is a code smell where a class has grown too large and tries to do too much.

We will see how we can refactor using the Extract Class technique for better maintainability and single responsibility.

Clean Code Reference

⚠️ Code Smell: Large Class
Refactoring: Extract Class
🎯 Goal: Single Responsibility Principle and focused classes

The Large Class code smell occurs when a class becomes too large and takes on too many responsibilities, violating the Single Responsibility Principle. The Extract Class refactoring technique breaks down large classes into smaller, focused classes that each have a single, well-defined responsibility.

The Code Smell: Large Class

Large Class is one of the most common code smells that indicates a violation of the Single Responsibility Principle. When a class grows too large, it becomes difficult to understand, test, and maintain. Large classes often contain multiple unrelated responsibilities, making them fragile and prone to bugs.

Symptoms

Impact

Too many fields and methods

Difficult to understand

Multiple unrelated responsibilities

Hard to test and maintain

Frequent changes affecting many methods

Higher bug risk

Here's a typical example of Large Class:

public class CustomerManager {
    // Customer data fields
    private String customerId;
    private String firstName;
    private String lastName;
    private String email;
    private String phone;
    private String address;
    private String city;
    private String state;
    private String zipCode;
    private String country;
    
    // Account management fields
    private double accountBalance;
    private String accountStatus;
    private Date accountCreatedDate;
    private Date lastLoginDate;
    private int loyaltyPoints;

    // Notification preferences
    private boolean emailNotifications;
    private boolean smsNotifications;
    private boolean pushNotifications;
    private String preferredLanguage;
    
    // What does this massive class do? Everything!
    
    // Customer data methods
    public void updatePersonalInfo(String firstName, String lastName, String email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
        validateEmail(email);
        logPersonalInfoUpdate();
    }
    
    public void updateAddress(String address, String city, String state, String zipCode, String country) {
        this.address = address;
        this.city = city;
        this.state = state;
        this.zipCode = zipCode;
        this.country = country;
        validateAddress();
        logAddressUpdate();
    }
    
    // Account management methods
    public void processPayment(double amount) {
        if (accountBalance >= amount) {
            accountBalance -= amount;
            updateLoyaltyPoints(amount);
            logTransaction(amount);
            sendPaymentConfirmation();
        } else {
            throw new InsufficientFundsException("Insufficient account balance");
        }
    }
    
    public void addFunds(double amount) {
        accountBalance += amount;
        logTransaction(amount);
        sendBalanceUpdateNotification();
    }
    
    public void updateAccountStatus(String status) {
        this.accountStatus = status;
        logStatusChange(status);
        if ("SUSPENDED".equals(status)) {
            sendAccountSuspensionNotification();
        }
    }

    // Notification methods
    public void updateNotificationPreferences(boolean email, boolean sms, boolean push) {
        this.emailNotifications = email;
        this.smsNotifications = sms;
        this.pushNotifications = push;
        logPreferenceUpdate();
    }
    
    public void sendNotification(String message, String type) {
        if ("EMAIL".equals(type) && emailNotifications) {
            sendEmailNotification(message);
        } else if ("SMS".equals(type) && smsNotifications) {
            sendSMSNotification(message);
        } else if ("PUSH".equals(type) && pushNotifications) {
            sendPushNotification(message);
        }
    }
    
    // Private helper methods (too many!)
    private void validateEmail(String email) { /* validation logic */ }
    private void validateAddress() { /* validation logic */ }
    private void logPersonalInfoUpdate() { /* logging logic */ }
    private void logAddressUpdate() { /* logging logic */ }
    private void logTransaction(double amount) { /* logging logic */ }
    private void logStatusChange(String status) { /* logging logic */ }
    private void logPreferenceUpdate() { /* logging logic */ }
    private void updateLoyaltyPoints(double amount) { /* loyalty logic */ }
    private void sendPaymentConfirmation() { /* notification logic */ }
    private void sendBalanceUpdateNotification() { /* notification logic */ }
    private void sendAccountSuspensionNotification() { /* notification logic */ }
    private void sendEmailNotification(String message) { /* email logic */ }
    private void sendSMSNotification(String message) { /* SMS logic */ }
    private void sendPushNotification(String message) { /* push logic */ }
}

In this example, the CustomerManager class is trying to do everything: manage customer data, handle account operations, and manage notifications. This violates the Single Responsibility Principle and makes the class difficult to understand, test, and maintain.

The Refactoring: Extract Class

The Extract Class refactoring technique breaks down large classes into smaller, focused classes that each have a single, well-defined responsibility. This improves code organization, testability, and maintainability.

Step by Step Refactoring Process:

  1. Identify distinct responsibilities within the large class.

  2. Group related fields and methods that belong together.

  3. Create new classes for each identified responsibility.

  4. Move fields and methods to their appropriate classes.

  5. Update the original class to use the new classes through composition.

Here's the refactored version:

The large class has been broken down into four focused classes:

  1. CustomerManager - Main coordinator class

  2. CustomerProfile - Manages customer personal information

  3. AccountManager - Handles account operations

  4. NotificationManager - Manages notification preferences and sending

1. CustomerManager - Main Coordinator Class

// Main class now focuses on coordinating other classes
public class CustomerManager {
    private CustomerProfile customerProfile;
    private AccountManager accountManager;
    private NotificationManager notificationManager;

    public CustomerManager(String customerId) {
        this.customerProfile = new CustomerProfile(customerId);
        this.accountManager = new AccountManager();
        this.notificationManager = new NotificationManager();
    }

    public void updatePersonalInfo(String firstName, String lastName, String email) {
        customerProfile.updatePersonalInfo(firstName, lastName, email);
    }

    public void updateAddress(String address, String city, String state, String zipCode, String country) {
        customerProfile.updateAddress(address, city, state, zipCode, country);
    }

    public void processPayment(double amount) {
        accountManager.processPayment(amount);
        notificationManager.sendPaymentConfirmation();
    }
}

2. CustomerProfile - Manages Customer Personal Information

// Focused class for customer profile data
public class CustomerProfile {
    private String customerId;
    private String firstName;
    private String lastName;
    private String email;
    private String phone;
    private String address;
    private String city;
    private String state;
    private String zipCode;
    private String country;
    
    public CustomerProfile(String customerId) {
        this.customerId = customerId;
    }
    
    public void updatePersonalInfo(String firstName, String lastName, String email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
        validateEmail(email);
        logPersonalInfoUpdate();
    }
    
    public void updateAddress(String address, String city, String state, String zipCode, String country) {
        this.address = address;
        this.city = city;
        this.state = state;
        this.zipCode = zipCode;
        this.country = country;
        validateAddress();
        logAddressUpdate();
    }
    
    private void validateEmail(String email) { /* validation logic */ }
    private void validateAddress() { /* validation logic */ }
    private void logPersonalInfoUpdate() { /* logging logic */ }
    private void logAddressUpdate() { /* logging logic */ }
    
    // Getters and other profile-related methods
}

3. AccountManager - Handles Account Operations

// Focused class for account operations
public class AccountManager {
    private double accountBalance;
    private String accountStatus;
    private Date accountCreatedDate;
    private Date lastLoginDate;
    private int loyaltyPoints;
    
    public void processPayment(double amount) {
        if (accountBalance >= amount) {
            accountBalance -= amount;
            logTransaction(amount);
        } else {
            throw new InsufficientFundsException("Insufficient account balance");
        }
    }
    
    public void addFunds(double amount) {
        accountBalance += amount;
        logTransaction(amount);
    }
    
    public void updateAccountStatus(String status) {
        this.accountStatus = status;
        logStatusChange(status);
    }
    
    public void updateLoyaltyPoints(double amount) {
        loyaltyPoints += (int) (amount * 0.1); // 10% of amount as points
    }
    
    private void logTransaction(double amount) { /* logging logic */ }
    private void logStatusChange(String status) { /* logging logic */ }
    
    // Getters and other account-related methods
}

4. NotificationManager - Manages Notification Preferences and Sending

// Focused class for notifications
public class NotificationManager {
    private boolean emailNotifications;
    private boolean smsNotifications;
    private boolean pushNotifications;
    private String preferredLanguage;
    
    public NotificationManager() {
        this.emailNotifications = true;
        this.smsNotifications = false;
        this.pushNotifications = true;
        this.preferredLanguage = "EN";
    }
    
    public void updateNotificationPreferences(boolean email, boolean sms, boolean push) {
        this.emailNotifications = email;
        this.smsNotifications = sms;
        this.pushNotifications = push;
        logPreferenceUpdate();
    }
    
    public void sendNotification(String message, String type) {
        if ("EMAIL".equals(type) && emailNotifications) {
            sendEmailNotification(message);
        } else if ("SMS".equals(type) && smsNotifications) {
            sendSMSNotification(message);
        } else if ("PUSH".equals(type) && pushNotifications) {
            sendPushNotification(message);
        }
    }
    
    public void sendPaymentConfirmation() {
        sendNotification("Payment processed successfully", "EMAIL");
    }

    private void logPreferenceUpdate() { /* logging logic */ }
    private void sendEmailNotification(String message) { /* email logic */ }
    private void sendSMSNotification(String message) { /* SMS logic */ }
    private void sendPushNotification(String message) { /* push logic */ }
}

Usage Example - Putting It All Together

public class Main {
    public static void main(String[] args) {
        // Create CustomerManager with refactored structure
        CustomerManager customerManager = new CustomerManager("CUST002");

        // Same operations, but now delegated to appropriate classes
        customerManager.updatePersonalInfo("Jane", "Smith", "[email protected]");
        customerManager.updateAddress("456 Oak Ave", "Chicago", "IL", "60601", "USA");

        customerManager.addFunds(750.0);
        customerManager.processPayment(200.0);

        customerManager.addOrder("ORDER003", 120.0);
        customerManager.addOrder("ORDER004", 80.0);

        customerManager.updateNotificationPreferences(true, true, false);

        System.out.println("Account Balance: $" + customerManager.getAccountBalance());
        System.out.println("Loyalty Points: " + customerManager.getLoyaltyPoints());
    }
}

Output:

Personal info updated for customer: CUST002
Address updated for customer: CUST002
Transaction logged: $750.0
Email sent: Account balance updated
Transaction logged: $200.0
Email sent: Payment processed successfully
Email sent: Order ORDER003 confirmed
Email sent: Order ORDER004 confirmed
Notification preferences updated
Account Balance: $550.0
Loyalty Points: 20

Benefits of Extract Class

Benefit

Description

Single Responsibility

Each class now has a single, well-defined responsibility, making them easier to understand and maintain.

Improved Testability

Smaller, focused classes are easier to test in isolation with fewer dependencies and edge cases.

Better Reusability

Extracted classes can be reused in other contexts without bringing unnecessary dependencies.

When to Apply Extract Class Refactoring

  • When classes have too many fields and methods (typically more than 20-30 methods).

  • When classes have multiple reasons to change (violating Single Responsibility Principle).

  • When classes contain groups of related methods that could be logically separated.

  • When classes are difficult to understand due to their size and complexity.

  • When classes have low cohesion where methods don't work together toward a common purpose.

Apply Extract Class refactoring to one large class in your current project today. Start with the class that has the most obvious distinct responsibilities.

Repository & Resources

Complete Code Examples: Clean Code Repository

Find the complete implementation of Large Class refactoring and other clean code techniques in our dedicated repository. Each example includes:

  • Before and after code comparisons

  • Unit tests demonstrating the improvements

Found this helpful? Share it with a colleague who's struggling with Large Classes. Got questions? We'd love to hear from you at [email protected]