2

I want to create a simple game and use one of IoC containers. Every game has players, so I want to inject them into Game class. The thing is, there could be different types of players. First player will be always Human (device owner;)), but his opponent: Human (play and pass), Bot or Online (human, but playing through internet).

Here is the code. There different players:

public class Human : IPlayer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public PlayerType Type { get; set; }
}

public class Bot : Human
{
}

public class OnlinePlayer : Human
{
}

public interface IPlayer
{
    int Id { get; set; }
    string Name { get; set; }
    PlayerType Type { get; set; }
}

and Game class:

public class Game : IGame
{
    public GameType Type { get; private set; }
    public List<IPlayer> Players { get; private set; }

    public Game(GameType type, List<IPlayer> players)
    {
        Type = type;
        Players = players;
    }
}

public interface IGame
{
    GameType Type { get; }
    List<IPlayer> Players { get; }
}

As you can see, I inject List of players in Game's container. Here is my question:

How can I resolve List<IPlayer> in case of differen type GameType?

if GameType = Single player -> Inject Human and Bot

if GameType = Pass and play -> Inject Human and Human

if GameType = Online game -> Inject Human and OnlinePlayer

Ish Thomas
  • 2,270
  • 2
  • 27
  • 57

2 Answers2

4

There's no reason to make it more complicated than it has to be. You can put something like this in an Abstract Factory:

if (gameType == GameType.Single)
    return new Game(
        GameType.Single,
        new List<IPlayer> { CreateHuman(); CreateBot() });
else if (gameType == GameType.PassAndPlay)
    return new Game(
        GameType.PassAndPlay,
        new List<IPlayer> { CreateHuman(); CreateHuman() });
else
    return new Game(
        GameType.Online,
        new List<IPlayer> { CreateHuman(); CreateOnlinePlayer() });
Community
  • 1
  • 1
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • In Game constructor, there will be also `IBoard` parameter. Which will be injected with `Board` class. Is it good to connect Abstract factory with IoC? – Ish Thomas Nov 07 '15 at 20:16
  • I looked closer to this topic and now it all makes more sense. One more question about your solution. You pass to `Game` constructor `CreateOnlinePlayer()` method. My concern is, that this method will be kinda blocking. I mean... probably (I haven't implemented it yet) it will return only when another user online wants to play. So this method will wait until server responds with particular player. Is it good to pass such method in `Game` constructor? – Ish Thomas Nov 11 '15 at 21:44
  • @IshThomas I don't think I'd deliberately write *any* code if it was clear to me that a method call could block for an arbitrary duration. That's a completely separate issue that has nothing to do with Dependency Injection. In the case of waiting for another player, I'd probably start looking into event-based architectures instead. – Mark Seemann Nov 12 '15 at 15:38
  • thanks for the tip. I think I will stick to your solution. Create an online player with just a Human player. And another method to Game interface "WaitForPlayers" - which will block till all players are present (2 players in my case, but it could be also used for more players). What do you think? I know it's a little bit out of scope (question was about list of players), but it kinda touches your solution. – Ish Thomas Nov 12 '15 at 21:45
3

You can use Ninject for this, i use this approach in some projects that i worked, and is very simple, see the Ninject project site: https://github.com/ninject/Ninject/wiki/Contextual-Binding

About your comment my suggest is separe the games types in concrete classes that implements IGame interface`, see the example:

public interface IGame
{
    GameType Type { get; }
    ReadOnlyCollection<IPlayer> Players { get; }
}

public class GameSingle : IGame
{
    public GameSingle(List<IPlayer> players)
    {
        Players = players.AsReadOnly();
    }

    public GameType Type => GameType.Single;
    public ReadOnlyCollection<IPlayer> Players { get; }
}

public class GameOnline : IGame
{
    public GameOnline(List<IPlayer> players)
    {
        Players = players.AsReadOnly();
    }

    public GameType Type => GameType.Online;
    public ReadOnlyCollection<IPlayer> Players { get; }
}

public class GamePlayAndPass : IGame
{
    public GamePlayAndPass(List<IPlayer> players)
    {
        Players = players.AsReadOnly();
    }

    public GameType Type => GameType.PassAndPlay;
    public ReadOnlyCollection<IPlayer> Players { get; }
}

Now in the module of the ninject :

        Kernel.Bind<IPlayer>().To<Bott>().WhenInjectedExactlyInto<GameSingle>();
        Kernel.Bind<IPlayer>().To<OnlinePlayer>().WhenInjectedExactlyInto<GameOnline>();
        Kernel.Bind<IPlayer>().To<Human>().WhenInjectedExactlyInto<GamePlayAndPass>();
        Kernel.Bind<IPlayer>().To<Human>();

And then you just need to define when and where inject the IGame concrete class XD.

Joel R Michaliszen
  • 4,164
  • 1
  • 21
  • 28