| home / programming / spring / 2 | [previous][next] |
|
|
The main problem so far with KnightOfTheRoundTable is with how it obtains a
HolyGrailQuest. Whether it is instantiating a new HolyGrail instance or obtaining
one via JNDI, each knight is responsible for getting its own quest (as shown in figure
1.2). Therefore, there is no way to test the knight class in isolation. As it

Figure 1.2 A knight is responsible for getting its own quest, through instantiation or some other means.
stands, every time you test KnightOfTheRoundTable, you will also indirectly test
HolyGrailQuest.
What’s more, you have no way of telling HolyGrailQuest to behave differently
(e.g., return null or throw a GrailNotFoundException) for different tests. What
would help is if you could create a mock implementation of HolyGrailQuest that
lets you decide how it behaves. But even if you were to create a mock implementation,
KnightOfTheRoundTable still retrieves its own HolyGrailQuest, meaning
you would have to make a change to KnightOfTheRoundTable to retrieve the mock
quest for testing purposes (and then change it back for production).
The problem, in a word, is coupling. At this point, KnightOfTheRoundTable is statically
coupled to HolyGrailQuest. They’re handcuffed together in such a way that
you can’t have a KnightOfTheRoundTable without also having a HolyGrailQuest.
Coupling is a two-headed beast. On one hand, tightly coupled code is difficult
to test, difficult to reuse, difficult to understand, and typically exhibits “whack-amole”
bugs (i.e., fixing one bug results in the creation of one or more new bugs).
On the other hand, completely uncoupled code doesn’t do anything. In order to
do anything useful, classes need to know about each other somehow. Coupling is
necessary, but it should be managed very carefully.
A common technique used to reduce coupling is to hide implementation
details behind interfaces so that the actual implementation class can be swapped
out without impacting the client class. For example, suppose you were to create a
Quest interface:

Then, you change HolyGrailQuest to implement this interface. Also, notice that
embark now returns an Object and throws a QuestException.

Also, the following method must also change in KnightOfTheRoundTable to be compatible with these Quest types:

Likewise, you could also have KnightOfTheRoundTable implement the following
Knight interface:

Hiding your class’s implementation behind interfaces is certainly a step in the right
direction. But where many developers fall short is in how they retrieve a Quest
instance. For example, consider this possible change to KnightOfTheRoundTable:

Here the KnightOfTheRoundTable class embarks on a quest through the Quest
interface. But, the knight still retrieves a specific type of Quest (here a HolyGrailQuest). This isn’t much better than before. A KnightOfTheRoundTable is stuck
going only on quests for the Holy Grail and no other types of quest.
The question you should be asking at this point is whether or not a knight should be responsible for obtaining a quest. Or, should a knight be given a quest to embark upon?
Consider the following change to KnightOfTheRoundTable:

Notice the difference? Compare figure 1.3 with figure 1.2 to see the difference in
how a knight obtains its quest. Now the knight is given a quest instead of retrieving
one itself. KnightOfTheRoundTable is no longer responsible for retrieving its
own quests. And because it only knows about a quest through the Quest interface,
you could give a knight any implementation of Quest you want. In a production
system, maybe you would give it a HolyGrailQuest, but in a test case you would
give it a mock implementation of Quest.
In a nutshell, that is what inversion of control is all about: the responsibility of coordinating collaboration between dependent objects is transferred away from the objects themselves. And that’s where lightweight container frameworks, such as Spring, come into play.
| home / programming / spring / 2 | [previous][next] |
Created: March 27, 2003
Revised: March 17, 2005
URL: http://webreference.com/programing/spring/2