Search code examples
c#dependency-injectioninversion-of-control

Inversion of control and internal classes


I've got three questions about inversion of control.

I'm building a chess program using C# and I have broken the code up into a number of class libraries. The idea is to encapsulate all of the logic about board positions and piece moving in one place, all of the logic about retrieving & writing game data in another, and all of the data about displaying a chess board & the pieces on it in a third (the UI).

I have one library that's the "business layer" of the application, containing interfaces & classes representing the game, the players, the board, the squares on the board, and pieces. There is a public interface for each of these objects. The classes that implement these interfaces are internal, however. It seems to me that this approach will prevent code outside of the library from instantiating or even knowing any of the internals, and is the right way to go. Clients should just use the methods & properties of the interfaces.

First question: Is this approach OK or does it somehow violate the DI principal?

I've been struggling with whether I should register the IPiece interface with the IoC, or even any of the interfaces other than IGame. This is because I want clients to only be able to create objects that implement IGame, and all of objects that implement the other interfaces come along with it. (I mean, you create a new game, you get a board with all 64 squares and the 32 pieces on it ready made. Or you retrieve a saved game & the board & the squares are created & the pieces are placed on it in their previous locations. No need for the client to do any of that).

Second question: Is this OK or does it violate the dependency injection principle?

The IPiece interface defines all of the functionality needed for a piece, without regard to what kind of piece it is. There is even a property that can be used by the UI to build the resource ID for the image to use to display the piece, so there's no need for the client to know what the class of a piece is.

The problem is that there is one abstract base class that implements the IPiece interface called Piece and 6 child classes that descend from it (Bishop, Knight, King, Queen, Rook and Pawn). I don't know how to register all of these classes against the IPiece interface, or even if I should.

I don't want clients to have to worry about what kind of piece they're working with, or to create them. When a new game is started, the pieces are created & placed on the board automatically by a method on the IGame interface. Loading a saved game, the pieces will be created and moved onto the board in their saved positions by another method in the same interface, without the client having to do anything other than retrieve the data and pass it to the method. This means that inside the chess engine library, the internal classes are instantiated without the use of the IoC.

The idea is the client just iterates all of the pieces & draws them on the on-screen board in the right place, without it needing to know the class of the object. All of the information the client needs is provided in properties defined by the IPiece interface.

Third question: Is this OK or should I define IBishop, etc. & register the classes to the interfaces IoC, even though the IoC mapping will never be used outside of the class library?


Solution

    1. Theoretically you should be fine. However, taking into account that the strongest benefit of IoC/DI is that it can make testing a lot easier, you're probably going to need a reference to the implementation when testing it. That being said, you can either resort to the InternalsVisibleToAttribute or you might not need to if you're not going to do unit testing (although I'd strongly recommend it);
    2. Your reasoning sounds good here too. You can do this in a minimalist fashion: start by not registering anything, running the code and registering types along as they're being required, and not earlier;
    3. Same as before, go on a need-to-know basis; or go with @Tone's suggestion if it's suitable in your case - have an IPieceFactory with an implementation to keep all the creation logic on.