Resolving God Objects:
Generalization to a Façade
Context
This article is part of the God object resolution series. To illustrate it, we use a running example through the whole series. So far, we dispatched the logics, the state, and the tests of the God object into new features. This article is about refactoring the God object into a façade, so we can use it as a feature aggregator. If we want to get rid of the God object, we can skip this phase and directly refactor its uses to replace it.
Problem
With the previous phases, we completely deprived the God object of the code and tests it has. If we want to keep it as a façade, we first need to make it usable as such.
Solution
We need to generalize it to take any implementation of the features it builds on. Starting from the current code:
class EmployeeManager {
private final Map<Integer, Employee> employees = new HashMap<>();
private final Employer employer = new EmployerImpl(employees);
private final EmployeeDetails details = new EmployeeDetailsImpl(employees);
private final SalarySettings salarySettings = new SalarySettingsImpl();
...
}
We first need to remove all the unnecessary fields, which are there only to instantiate the features.
Indeed, we should receive instances of the Employer
, EmployeeDetails
, and SalarySettings
interfaces, whatever their dependencies are.
Having an employees
field makes sense only because we use the current implementations of these interfaces.
To generalize that, we need to explicit the empty constructor:
class EmployeeManager {
// Employees field has become a simple variable in the constructor
private final Employer employer;
private final EmployeeDetails details;
private final SalarySettings salarySettings;
public EmployeeManager() {
Map<Integer, Employee> employees = new HashMap<>();
this.employer = new EmployerImpl(employees);
this.details = new EmployeeDetailsImpl(employees);
this.salarySettings = new SalarySettingsImpl();
}
...
}
Notice how the constructor allows us to create a simple variable employees
to use inside the constructor.
The field can be removed, since this instance is only useful for instantiating our features, which is done in this constructor.
The next step is to add a constructor to take other instances of these interfaces:
public EmployeeManager(Employer employer, EmployeeDetails details, SalarySettings salarySettings) {
this.employer = employer;
this.details = details;
this.salarySettings = salarySettings;
}
With this new constructor, any code can instantiate the three interfaces and provide its own implementation. Old code relying on the empty constructor may be refactored to use the new one. Although we will do that in the next phases, we can already help on that matter by deprecating the empty constructor:
@Deprecated
public EmployeeManager() {
All the uses of the empty constructor will now generate a warning that helps to spot the codes to refactor. If your code is used in a library, it also allows you to help your users to spot the refactyoring to do before to pass to a version where this constructor disappear. At this point, the God class can be used as a real façade and tested as such. So let's test it!