Skip to main content
pragmatic clean code minimizing cognitive load in production java

Dealing with God Classes

4 min read Chapter 22 of 25
Summary

God Classes, violating SRP, drive high maintenance costs....

God Classes, violating SRP, drive high maintenance costs. LCOM and variable tracing identify clusters. The Extract Class pattern and interface shims (Strangler) enable incremental refactoring, reducing cognitive load and defect density to improve economic sustainability.

Dealing with God Classes

God Classes, those behemoths of code that have grown too large and unwieldy, taking on too many responsibilities and violating the Single Responsibility Principle (SRP), are a major contributor to the high maintenance costs of software systems. With over 70% of the total software lifecycle expenditure going towards maintenance, and engineers spending 60-90% of their time reading and understanding code rather than writing it, the economic consequence of such classes cannot be overstated. The Read-to-Write ratio of source code is approximately 10:1, indicating that for every line of code written, ten lines are read, making the cognitive load imposed by God Classes a significant concern.

One of the primary technical metrics used to identify candidates for splitting God Classes is the Lack of Cohesion of Methods (LCOM). A high LCOM score indicates that the methods of a class do not share the same instance variables, suggesting that the class is performing multiple, unrelated tasks. This not only increases the cognitive load on developers trying to understand the class but also makes the class more prone to errors and defects. High cyclomatic complexity at the class level is statistically correlated with higher defect density, further emphasizing the need to address God Classes.

To tackle God Classes, developers can employ several strategies, starting with variable usage tracing. By mapping which methods access which instance variables, developers can identify conceptual clusters within the class—groups of methods and variables that frequently interact with each other but rarely with other parts of the class. These clusters often signal potential new classes that can be extracted from the God Class, thereby reducing its size and increasing its cohesion.

The Extract Class pattern is a primary refactoring technique used to resolve God Classes. This involves moving a cluster of variables and their methods to a new class, thereby reducing the original class’s size and increasing its adherence to the SRP. For instance, consider the UserManager class, which might initially look like this:

// Before Refactoring: God Class with mixed responsibilities
public class UserManager {
    private String username;
    private String password;
    private String emailServerHost;
    private int emailPort;

    public void changePassword(String newPass) { /* uses username, password */ }
    public void sendWelcomeEmail() { /* uses username, emailServerHost, emailPort */ }
}

Through variable tracing and affinity grouping, we might identify a cluster related to email configuration and functionality. Extracting this into a separate EmailService class could result in the following refactored code:

// After Refactoring: Extracted EmailService class
public class EmailService {
    private final String host;
    private final int port;
    public EmailService(String host, int port) { this.host = host; this.port = port; }
    public void send(String recipient, String message) { /* email logic */ }
}

public class User {
    private String username;
    private String password;
    // Logic moved to EmailService, reducing User cognitive load
}

This refactoring not only reduces the size of the original class but also decreases the cognitive load by minimizing the number of variables a developer must consider when working with the User class. The EmailService class, now focused on email-related logic, improves the overall cohesion of the system.

In addition to the Extract Class pattern, techniques like creating an interface shim for the God Class can help decouple callers while the class is being refactored. This approach, part of the Strangler Fig pattern, allows for incremental migration of legacy code to new, smaller classes without disrupting the existing system. It enables dark launching for testing and connects well with local reasoning and technical debt management strategies.

The process of breaking down God Classes into more manageable, cohesive units requires careful analysis and planning. By leveraging tools like SonarQube or ArchUnit for automated detection of God Classes and high LCOM scores, developers can identify potential candidates for refactoring. However, the actual refactoring process must be guided by a deep understanding of the code’s structure and the economic consequences of maintaining large, complex classes.

In conclusion, dealing with God Classes is essential for reducing software maintenance costs and improving code clarity. By applying techniques such as variable usage tracing, affinity grouping, and the Extract Class pattern, developers can break down these monolithic classes into smaller, more cohesive units. This not only improves the maintainability of the software system but also reduces the cognitive load on developers, making it easier to understand and modify the codebase over time.

Sources