Reading the wikipedia entry about God Objects, it says that a class is a god object when it knows too much or does too much.
I see the logic behind this, but if it's true, then how do you couple every different class? Don't you always use a master class for connecting window management, DB connections, etc?
The main function/method may know about the existence of the windows, databases, and other objects. It may perform over-arching tasks like introduce the model to the controller.
But that doesn't mean it manages all the little details. It probably doesn't know anything about how the database or windows are implemented.
If it did, it could be accused of being a God object.
A god object is an object that contains references, directly or indirectly, to most if not all objects within an application. As the question observes, it is almost impossible to avoid having a god object in an application. Some object must hold references to the various subsystems: UI, database, communications, business logic, etc. Note that the god object need not be application-defined. Many frameworks have built-in god objects with names like "application context", "application environment", "session", "activator", etc.
The issue is not whether a god object exists, but rather how it is used. I will illustrate with an extreme example...
Let's say that in my application I want to standardize how many decimal places of precision to show when displaying numbers. However, I want the precision to be configurable. I create a class whose responsibility is to convert numbers to strings:
class NumberFormatter {
...
String format(double value) {
int decimalPlaces = getConfiguredPrecision();
return formatDouble(value, decimalPlaces);
}
int getConfiguredPrecision() {
return /* what ??? */;
}
}
The question is, how does getConfiguredPrecision
figure out what to return? One way would be to give NumberFormatter
a reference to the global application context which it stores in a member field called _appContext
. Then we could write:
return _appContext.getPreferenceManager().getNumericPreferences().getDecimalPlaces();
By doing this, we have just made NumberFormatter
into a god object as well! Why? Because now we can (indirectly) reference virtually any object in the application through its _appContext
field. Is this bad? Yes, it is.
I'm going to write a unit test for NumberFormatter
. Let's set up the parameters... it needs an application context?! WTF, that has 57 methods I need to mock. Oh, it only needs the pref manager... WTF, I have to mock 14 methods! Numeric prefs!?! Screw it, the class is simple enough, I don't need to test it...
Let's say that the application context had another method, getDatabaseManager()
. Last week we were using SQL, so the method returned an SQL database object. But this week, we've decided to change to a NoSQL database and the method now returns a new type. Is NumberFormatter
affected by the change? Hmmm, I can't remember... yeah, it might be, I see it takes an application context in the constructor... let me open the source and take a look... nope, we're in luck: it only accesses getPreferenceManager()
... now let's check the other 93 classes that take an application context as a parameter...
This same scenario occurs if a change is made to the preferences manager, or the numeric preferences object. The moral of the story is that an object should only hold references to the things that it needs to perform its job, and only those things. In the case of NumberFormatter
, all it needs to know is a single integer -- the number of decimal places. It could be created directly by the application god object who knows the magic number (or the pref manager or better still, numeric prefs), without turning the formatter into a god object itself. Furthermore, any components that need to format numbers could be given a formatter instead of the god object. Wins all around.
So, to summarize, the problem is not the existence of a god object but rather the act of conferring god-like status to other objects willy-nilly.
Incidentally, the design principle that tackles this problem head-on has become known as the Law of Demeter. Or "when paying at a restaurant, give the server your money not your wallet."
In my experience this most often occurs when you're dealing with code that is the product of "Develop as you go" project management (or lack there of). When a project is not thought through and planned and object responsibilities are loose and not delegated properly. In theses scenarios you find a "god-object" being the catchall for code that doesn't have any obvious organization or delegation.
It is not the interconnectedness or coupling of the different classes that is the problem with god-objects, it's the fact that a god-object many times can accomplish most if not all responsibilities of it's derived children, and are fairly unpredictable (by anyone other than the developer) as to what their defined responsibilities are.
Simply knowing about "multiple" classes doesn't make one a God; knowing about multiple classes in order to solve a problem that should be split into several sub-problems does make one a God.
I think the focus should be on whether a problem should be split into several sub-problems, not on the number of classes a given object knows about (as you pointed out, sometimes knowing about several classes is necessary).
Gods are over-hyped.