What is the God Object antipattern, and how would you break up a class that has become one?
A God Object is a class that holds most of the system's state and implements most of its behaviour. It tends to be enormous, it has many dependencies, and it is the thing everything else talks to. The name comes from the concentration of responsibility, and it is the classic symptom of a design that started clean and grew without being cut back.
public class Main {
public static void main(String[] args) {
App app = new App(new Logger(), new Store());
app.run();
}
}
class Logger {
void log(String line) { System.out.println("log: " + line); }
}
class Store {
void save(String data) { System.out.println("saving " + data); }
}
class App {
private final Logger logger;
private final Store store;
App(Logger logger, Store store) { this.logger = logger; this.store = store; }
void run() {
logger.log("starting");
String data = "users";
store.save(data);
logger.log("done");
}
}The refactored version splits the responsibilities into three pieces. The logger knows how to write a log line, the store knows how to persist data, and the application class holds the collaborators and orchestrates the run. The output is identical to the original god object version, but each class now has one reason to change and each can be tested on its own.
There are two ways this antipattern appears. One is a god class, where the problem is simply that everything is in one file. The other is worse: a god object that other classes read from. It becomes a global state holder, and then the order of operations in the whole program starts to matter, which makes concurrent access dangerous and testing painful.
The cause is usually a lack of decisions about ownership. When nobody decides that logging belongs to a logger and persistence belongs to a store, everything drifts into whatever class happens to be in the middle. The fix is to make those ownership decisions explicitly, one responsibility at a time, and to move state to the class whose behaviour depends on it.
The practical approach for a large codebase is to split incrementally. Extract one responsibility at a time, keep the behaviour identical, and let the tests confirm that nothing changed. Trying to redesign the whole thing in one pass is how god objects turn into slightly different god objects.