XNA Game – Rotation – Adding AutoFac IoC Container – Part 11


Now that I have the game starting to take shape (well that animations working and drawing the board) I have decided to put in an IoC container to get rid a lot of the custom wire up code, aka the following code block:

_board = new BoardFactory().Create();
var boardFiller = new BoardFiller(new StandardTileFactory(new LetterLookup()));

Func getMainSelectedSquare = () => _board.GetMainSelectedSquare();

var textureLoader = new TextureLoader(s => Content.Load(s));
		    _itemDrawerFactory =
		        new ItemDrawerFactory(new List
		                                      new SquareDrawer(
		                                          new TileTextureFactory(new List
		                                                                         new BlankTileTextureCreator(textureLoader),
		                                                                         new StandardTileTextureCreator(textureLoader)
		                                          new SquareColourSelector(),
		                                          new SquarePositionCalculator(getMainSelectedSquare),
		                                          new SquareOriginCalculator(getMainSelectedSquare))

		    var itemAnimatorFactory = new ItemAnimatorFactory(new List {new RotationAnimator()});

            _animationEngine = new AnimationEngine(itemAnimatorFactory, _itemDrawerFactory, _board.GetAnimatables);

            _currentPos = new Point(4, 4);
            _squareSelector = new SquareSelector();
            _squareSelector.Select(_board, _currentPos.X, _currentPos.Y);

            _selectionRotatator = new SelectionRotatator();

I think you will agree that the above code is a bit long winded because of course we are providing all of the dependencies manually to every constructor.

The first thing that I want to do is write a module to install classes by convention.  By registering by convention I mean if I have an interface called ITest and it’s implemented by a class called Test then that class will get installed automatically as it’s name is the same as the interface without the leading ‘I’.

Registering classes by convention is a good thing to do as one of the main reasons we use dependency injection (other than to reduce coupling) is to allow easy unit testing of our components.  However, when we come to use these components in the application it can get a bit long winded to register all of them one by one.   I know most IoC containers support auto registration of all classes by scanning your assemblies but this is not what I want either.  The reason is that if I implement an interface with a class that I intend to always use as the implementation of that interface then I will give it a name so that it’s installed by convention.  If I intend to swap the component out then I will use a different class name so that I have to implicitly install it.  I know you can never really say that you will always use a certain concrete implementation of an interface but what I am saying is that for the foreseeable future that is the implementation I will be using.

The IoC container that I have chosen to use is AutoFac.  Mainly because I have quite a bit of experience with using Castle and AutoFac and I prefer the syntax of AutoFac over Castle.  For what I want to do with IoC any of the main IoC containers would do the job.

Ok so on with writing my module (that is the name of installers in AutoFac) to install classes by convention I need a unit test.  This is quite a simple test:

var container = default(IContainer);
var result = default (ITest);

"Given I have installed the instant convention installer with this assembly".Context(() =>
                    var containerBuilder = new ContainerBuilder();
                        .RegisterModule(new InterfaceConventionModule(new[] { typeof(ITest).Assembly }));
                     container = containerBuilder.Build();

"When I resolve an ITest interface".Do(() => result = container.Resolve());

"Then the returned type should be a Test class".Observation(() => result.ShouldBeOfType());

The reason that my installer takes an array of assemblies is that it provides me with a neat way of choosing which assemblies to consider when installing by convention.  So when I am unit testing (as I am above) all I need to do is pass in the current assembly.  Obviously in the main code I will pass in all of the assemblies that I am using in the game. Implementing this installer is a pretty simple method:

protected override void Load(ContainerBuilder builder)
        .Where(t => t.GetInterfaces().Any(i => i.Name.TrimFirstChar('I').Equals(t.Name)))

TrimFirstChar is a simple extension method that I have written on string that simply chops off the first character if the character passed in matches, if it doesn’t then it returns the string that’s passed in.

The only other thing to do is write a few installers to install the classes that haven’t been installed by convention.  The installers are pretty mundane so I won’t list them here, if you want to see them pull down the latest version of the code and view branch part11.

Now that we can install all of the classes that we need all we need to do is fire up the container in the program start method, resolve the RotationGame class and call run on it:

using(var container = new Builder().Build())
    var childContainerBuilder = new ContainerBuilder();

    using (var game = container.Resolve())

Pretty straight forward. Now in our game we can specify all of the dependencies in the constructor and AutoFac will do the rest.  Nearly all of the first block of code can be removed 🙂

In the future I want to use the IoC container to swap out some of the components to make the game more difficult as the player progresses. This means that all of my code should stay the same and to progress levels all I should have to do is register different classes with the container. That’s the plan anyway for a later post.

As always if you want to get the latest version of the code you can do so from github, I have marked the code at this point as part11.

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s