I read Sam’s post on Contextl IoC, questioning its benefits. I’ve been skirting around this kind of thing for a while. I like the idea of being able to package all the things which a class needs in a box which ‘belongs’ to it.
Say my main class, Gamey
, knows how to start new Tetris games.
It needs a game factory which creates games.
The game factory needs to know about user preferences, the gui components to be used to draw the game, the score system, the rules, the timer which drops the shapes, the factory which makes the shapes, etc.
So I need to construct the GameFactory as:
new GameFactory(UserPreference preference, GuiComponentFactory factory, Scorer scorer, RulesEngine rulesEngine, Timer timer)
.
and ditto for the game it creates.
Except that what I really want is to be able to create different kinds of games, so I can choose to play either Tetris or PacMan, and use a different game factory for each one. They still each need the same stuff – a component factory, a preference, a scorer, a rules engine, a timer etc – so now I have to duplicate that factory constructor for both types of factory; the TetrisGameFactory
and the PacmanGameFactory
.
Now I want to add multiplayer support, so that each monster killed appears in another person’s game, or each line deleted causes extra blocks to appear elsewhere. Now I need to add MultiplayerSupport
to:
- Two GameFactory constructors
- Two Game constructors
- The calls for Game construction
- probably some abstract stuff in some
AbstractGameFactory
, and someAbstractGame
, etc.
And I don’t even know, in the GameFactory
, which bits are related to the Game and which bits are being passed further down, to the Game’s pieces, like the Tetris shapes and the Pacman ghosts, which are also factory-driven but which need to be created by type. Oops, forgot the PiecesFactory
. That’s another change, then.
I really like the concept of a Game.Context
and a Shape.Context
and a Tetris.Context
and a Gamey.Context
. They can even extend each other or contain each other if required – for instance, the Tetris.Context
extends a Game.Context
, and contains a ShapeFactory
which needs a Shape.Context
. Then, when I want to change a dependency, all I need to do is change one context and its construction. I don’t need to pass it through all the layers, abstract and otherwise.
This solves a real problem I’ve been coming across when trying to do behaviour driven design. I don’t want to have to think about the behaviour of my shapes when I’m still thinking about the responsibilities of my games. If I can give the game an empty Game.Context
to start with and never worry about how the GameFactory
constructs the Game
again, then that’s one less piece of code to change every time I decide to add a bit of functionality.
The Game.Context
shouldn’t be responsible for using the Game’s tools for it, though. It’s just a little container; nothing else. It should probably just have get
methods (ugly, but suggestions welcome; will think on it) for each of the tools it requires.
I’m not even sure that the Game.Context needs to be an interface. It never needs to be mocked – you mock the tools underneath, not the container.
Stacy reckons there has to be another way to prevent the duplication which keeps cropping up in my normal dependency injected code. I also reckon ‘context’ is a poor name; ‘toolbox’ might be better.
What you call a Context, we call a Config, with typical accessors like getLog(), getInitProperties(), etc.
The ease of maintaining a contract of the form:
public Thing(ThingConfig config) ...
over
public Thing(Log log, Properties props, FooFactory factory....
becomes apparent pretty quickly. Good times.