Resolving God Objects:
Usage Refactoring
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. We also transformed it into a façade and tested it to use it as a feature aggregator. This article is about refactoring the code that uses it by using specific features instead when relevant.
Problem
The façade, although reliable, is not always the most relevant class to use. Some pieces of code might focus on specific methods only, which correspond to specific —if not one— features only. We must keep the dependencies to the minimum, as stated by the Interface Segregation principle.
Solution
As long as the old constructor of the God object is used, it remains a God-like object which decides on its own behalf which implementation to use. Several situations can occur, each with its own refactoring.
If you receive the God object as a parameter but use only the methods of a single feature, then you should instead request the (interface of the) feature itself. In the parent method, which provides the God object, you will need instead to provide the feature. The simplest and most relevant way to do so is to add to the God object a getter on the feature and to use it in the parent method. Indeed, the God object is now a facade which brings several features together, thus it makes sense to access directly these features from the façade. If you cannot change the implementation, an alternative solution is to implement the feature interface by calling the façade methods, like in the next situation.
If you receive the God object as a parameter but use only few methods of several features, then think about using your own, specific façade. In the parent method, which provides the God object, you will need to implement the client façade by calling the relevant methods of the God object, like the God object calls the dedicated features. You may think that it is odd to have a façade over a façade, and indeed it should be rather exceptional. If you happen to repeat this process several times, you may need to think again about the relevance of the client façade: is it really worth it? If it is, then you may think again about how to split your God object: did you choose the most relevant features?
If you receive the God object as a parameter and use most of its methods, then the façade makes sense. You may ask yourself whether it is good enough to provide the features separately, which depends on how you use them. If you use the parameter directly without cascading it elsewhere, then the façade has a small added value and you may get rid of it. Otherwise the façade helps you to share the methods in a single parameter.
If you instantiate the God object but use only the methods of a single feature, then you should instantiate the feature itself directly. You may need to pay attention to its external dependencies, but there is a good chance you already did it for the God object.
If you instantiate the God object but use only few methods of several features, then try to instantiate the features directly. If you send them together to other methods, a façade makes sense to send them as a single parameter. However, you may prefer to use your own façade to not depend on too much methods (Interface Segregation principle).
If you instantiate the God object and use most of its methods, then use the façade. If you send them together to other methods, the façade makes sense to send them as a single parameter.
A good practice is to start from the leaves of your code: the methods which use the God object without providing it as a parameter to other methods. Once you have refactored them, then you should go to their parents, refactor them, go a step further, and so on recursively. This way, you allow yourself to focus on your actual needs, and progressively spread it through the whole software.
You may instantiate the façade with only the relevant features for you. For the features you don't need, instantiate them with the "non implemented" classes. They will give you a relevant exception if you use them accidentaly.
At this point, it is only a matter of time before the deprecated constructors are not used anymore. Either you spend time doing this refactoring, or you advertise your changes to the relevant people. Once you confirmed that the deprecated constructors are not needed anymore, remove them. At that time, the God object will be a closed case. Good job!