Steaming stinking jungle crushing all about you, insects infesting the air, strange grunts shuddering in the foliage, you pause deep in uncharted refactoring, far from respite, far from home, far from help. Sweat oozes from all pores. Your machete, notched and dulled from ten thousand hacks, hangs weary in your calloused grip while your mind mumbles to itself, exhausted. Behind you no track records your passing: the countless vines severed, the countless dependencies torn asunder, the countless decrepit classes heaved aside, none of these has changed the tangling madness. All your agonized restructuring seems wasted, meaningless, amid the yelping brutes scuttling unglimpsed beyond the trees.
Then you see the darkness.
Lost in toil, you had not noticed it as you approached, its form an expansive yet subtle shading. Dread floods your soul. You stagger through the treeline, drawn to the horror, and the light begins to fail with each step. Weak slashing fails to keep the briars from your flesh but you push on. You have to see it. You must see it. A final wall of sticky vegetation parts and you stumble through. The vista opens before you. The monster reveals itself.
Immeasurably vast, the foul beast sprawls from forest floor to canopy, smothering the sky. Tendrils snake everywhere, radiating from the central denseness to choke every class they touch. Worse still, fat tuberous dependencies thrash towards the god object's gigantic core from all sides, an insanity of twisting and coiling and piercing.
Defeated, you collapse to your knees, machete spilling from your hand. Despair pounces. You bow your head.
There, on the ground before you, something glints, something silver and out-of-place. With your last strength you reach out to pick up the metal tag. Faint writing scores its surface.
It reads, "MavenProject.java".
To the term, "God object," many object. The animal in question is not at all an object but a class, not an electrically-driven choreography of registers pinging data across high-speed buses but a file of text, albeit a commodious one.
Nor would many quite agree with the honourable Wikipedia's claim that our quarry need have, " ... most of a program's overall functionality ... coded into a single 'all-knowing' object, which maintains most of the information about the entire program and provides most of the methods for manipulating this data." Given today's corporate behemoths, a single class housing the better part of a banking system, for example, would be a thing of splendid wonder.
Instead, most would consider a god object to be any class that was just doing far too much, had far too many responsibilities or just had far too many lines of code.
This begs the question: what is far too much?
Few would consider a class of two hundred lines of text far too anything. It might be, of course: outliers and rare exceptions exist, but a reasonable class of two hundred lines would hardly raise suspicions purely because of its size.
A class of five hundred lines might meet some jeering, some stern interrogation; yet it, too, could sail through a review if justified by a knowledgeable designer. There are always reasons for these things.
At a thousand lines long, however, a class has crossed an unspoken boundary. Good taste has been snubbed, good breeding insulted. Someone's whipped out a can of lager at the wine-tasting. A thousand lines just seems so breakupable. Has this class really so focused a single yet gargantuan responsibility that it could not be split in two? Really?
"Now you're just being silly," is certainly the politest version of comment heard when a class of fifteen hundred lines steps out of the taxi and onto the reviewers' red carpet. The paparazzi flash their cameras but disbelief rather than admiration motivates their whirring shutters.
Two thousand lines perhaps - just perhaps - denotes ascension to godliness. It may not accommodate most of the program's functionality but dear, oh dear a class wily enough to evade reviewer after blade-wielding reviewer and grow so large almost deserves a shrine or two erected in its honour. That such a class might justify its own non-decomposability would strain the belief of even the most gullible software architect.
God objects, furthermore, fall into two categories.
The first is the autonomous god object, which, while screaming demented abuse at all good structure principles, at least has the grace to do so by itself, secluded in a corner somewhere. Few classes depend on this type of god object; one or two other classes must prod it into action, after which it will bluster about doing an enormous amount of stuff, but at least splitting it into smaller classes will not ripple back up through the system precisely because so few other classes depend on it. Its relative isolation makes this type of god object the lesser of the two evils.
The communal god object, the second category, boasts a vast number of dependent classes. This type can have twenty, thirty or even forty other classes directly dependent on it, all of which cover their eyes and inhale through clenched teeth when a programmer attempts a decomposition. Listen for the crunch-crunch-crunch as you approach for the floors about these god objects lie littered with the shards of shattered careers.
Have you ever seen one?
Have you ever seen a communal god object?
If not, sit back and find something tough to bite down on ...
Figure 1: The MavenProject god object.
No image guarantees the presence of the beast. Structural diagrams can only ever raise not answer questions. A cursory trek through the
MavenProject class itself reveals, however, a full 2200 lines of code, four times what some consider a healthy size and practically a package in its own right.
The question is: when face-to-face with such a bruiser, what should you do?
Programmers usually do one of three things. First, they may simply look the other way, a common solution. Second, they may start ripping out what methods they can into smaller classes. Both options have their merits.
The third option, though, often goes overlooked. This option acknowledges the dynamism of the god object; they grow so big because they do just that: they grow. They seldom stand still and they usually do not stop gobbling up functionality just because you decide to step in and take control.
With the presumption of continued growth, option two looks shaky: like a hydra, the god object watches the classes you chop off soon themselves begin to writhe and gobble and grow. Cohesion degrades and with all those dependencies skewering directly into the new implementation classes coupling becomes problematic.
The third option recommends that you do not reduce the size of the god object at all.
At least initially.
Instead, you merely begin creating small interfaces - implemented by the god object - and have erstwhile clients of the god object redirect their attention to these interfaces.
Opinions divide on the nature of these small interfaces.
One camp suggests analyzing the god object's functions and grouping them semantically. This has the advantage of keeping the new interfaces disjoint, sharing no common function declarations, but the disadvantage of having clients receive interfaces all of whose functions they probably will not use.
The other camp suggests creating a new interface for each client package (or significant class), making the interfaces more client- than provider-specific. This has the advantage that clients use precisely and fully the interfaces that they receive but the disadvantage that many interfaces might declare the same functions.
In practice, some begin with the second approach and when finished gather duplicated function declarations into common, extended interfaces.
However achieved, this is but the first step. Once the penultimate dependency on the god object snaps still the knives should remained sheathed. The beast must first be made package-private (Java's default access modifier) so that future clients cannot bypass the interfaces and then preferably be moved to its own, new package, leaving the interfaces behind. Only then, when the god object is isolated from all else and with the interfaces now revealing themselves as a Facade, can the dissections begin.
That might sound like a lot of work.
No one said this would be easy.
The final configuration, however, offers a Facade of interfaces to clients untroubled by decoupled implementation refactorings, and a separate package from which unearthly screams radiate throughout the entire program. For a while.
Just for fun, we can take an X-ray of our
MavenProject class to see what its guts look like. Often large classes suggest their own extractions and as we see from figure 2 that
deepCopy() function looks tempting.
Figure 2: MavenProject's functions.
God objects: don't.
No classes were harmed during the writing of this post.
* Dagnabbit! Pipped to the post (title): aaronblohowiak