Followup to Sam’s post, Sam’s other post and Liz’s post about Sam’s posts.
The contextual IOC pattern looks to me like a solution to the ‘problem’ of Dependency Injection based IOC, namely that all an object’s dependencies are explicitly named. But wait a minute, IOC was a solution to the problem of having objects whose dependencies weren’t clear in the first place.
Dependency injection bothers me because it encourages an object to have too much control. To shamelessly steal Liz’s example code:
new GameFactory(UserPreference preference, GuiComponentFactory factory, Scorer scorer, RulesEngine rulesEngine, Timer timer)
The reason GameFactory
needs all that stuff is because it is presumably going to pass it down to the Game
objects it creates. That is fine, and 5 parameters in a constructor isn’t setting off any code-smell alarms for me. I wouldn’t do it that way however. For a start, I question the need for a factory. Presumably somewhere there is a UI showing a list of possible games waiting for the player to choose one. I see no benefit in having an abstract factory when what I want to do in response to a user clicking ‘Tetris’ is a new TetrisGame()
. The indirection of the factory is just getting in the way.
I could see how you might want a more configurable list of games, in which case possibly a GameCollection
containing a TetrisFactory
, a PacmanFactory
etc. might work, if the game objects were too heavyweight to simply pre-instantiate at startup.
That’s not really where I want to go with this post though. The thing that really caught my eye was the Timer
parameter. I’m being a bit mean picking on this one as its easy target, and design is subjective, but it demonstrates one of my concerns with IOC quite nicely. Clearly a Game
object requires some notion of the passage of time, as many games are based on things happening periodically. In the Tetris example, it has to move a block down the screen and check to see if it’s landed on the pile. However, injecting a Timer
is not what I would do. What the game object really wants is for something else to tell it when time has passed. Something external. The code might look like this:
public abstract class Game implements TimeDependent { } public interface TimeDependent { void clockTick(long millisSinceLastTick); // or possibly, for more OO flexibility void clockTick(TimeInstant aFrozenMomentInTime); }
This style satisfies all the requirements of testability, with the added simplification of removing one of the dependencies from the constructor.
You could do the same with Scorer
. The game could expose a public TetrisScore basicScore()
method, that a collaborator can call, and do its own post-processing on before showing it to the user.
UserPreferences
is tougher. I’d agree that the game wants to ask a number of questions of the preferences object, so it should probably be given it.
To conclude, we build object-oriented systems. Its ok for an object to need help from collaborating objects, without having to own them all, or even know who is helping it.