【问题标题】:Should I use Constructor Injection or IoC.Resolve?我应该使用构造函数注入还是 IoC.Resolve?
【发布时间】:2016-02-20 13:26:12
【问题描述】:

我正在构建非常简单的棋盘游戏(只是为了学习一些新东西)。它将是跨平台的(使用 Xamarin 编写)。我已经编写了游戏的核心,但我不确定(仍然)应该使用 构造函数注入 还是 IoC 解析。现在我使用 IoC.Resolve 然后传递参数。

这是我的代码。我有 Game 有 2 个依赖项和类型。我有工厂来创建游戏和播放器:

Abc.MyGame.Domain

  • 接口

    public interface IGame
    {
        GameType GameType { get; }
        IGameBoard GameBoard { get; }
        List<IPlayer> Players { get; }
    
        // Other stuff...
    }
    
    public interface IGameBoard
    {
        //board stuff...
    }
    
    public interface IPlayer
    {
        int PlayerId { get; set; }
        PlayerType PlayerType { get; set; }
    }
    
  • 工厂接口

    public interface IGameFactory
    {
        IGame CreateGame(GameType type, IGameBoard board);
    }
    
    public interface IPlayerFactory
    {
        IPlayer CreatePlayer(PlayerType type, int id);
    }  
    
  • 工厂

    public class GameFactory : IGameFactory
    {
        public IGame CreateGame(GameType type, IGameBoard board)
        {
            switch (type)
            {
                case GameType.SinglePlayer:
                    return new MyGame(type, board,
                        new List<IPlayer> { CreateHuman(1), CreateBot(2) });
                case GameType.TwoPlayers:
                    return new MyGame(type, board,
                        new List<IPlayer> { CreateHuman(1), CreateHuman(2) });
                case GameType.Online:
                    return new MyGame(type, board,
                        new List<IPlayer> { CreateHuman(1), CreateOnlinePlayer(2) });
    
            }
            return null;
        }
    }
    

然后,有一个 API。由 UI 使用:

Abc.MyGame.API

public class GameAPI
{
    public IGame CurrentGame { get; set; }

    public IGame CreateGame(IGameFactory gameFactory, GameType type, IBoard board)
    {
        CurrentGame = gameFactory.CreateGame(type, board);
        return CurrentGame;
    }

    // Other stuff, like make move etc...
}

...和我的用户界面:

Abc.MyGame.UI.WinForms

public partial class Form1 : Form
{
    private GameAPI api = new GameAPI();
    IKernel kernel = new StandardKernel();

    public Form1()
    {
        InitializeComponent();

        // Load all dependencies
        var modules = new List<INinjectModule> { new GameModule() };
        kernel.Load(modules);
    }

    private void buttonStartGame(object sender, EventArgs e)
    {
        IGameBoard board = kernel.Get<IGameBoard>(
            new ConstructorArgument("width", 7), 
            new ConstructorArgument("height", 6)
        );

        api.CreateGame(kernel.Get<IGameFactory>(), GameType.TwoPlayers, board);
    }
}

我需要在我的IGame 中添加IGameBoard。只有那里。我需要将棋盘和玩家注入IGame

这是我的问题/疑问: 我真的需要在程序的最“前端”解析IGameBoard 吗?当有人单击“开始游戏”按钮时,我在UI 中解决了它。然后我通过API 传递这个板,然后我将它传递给GameFactory,然后我将它传递给Game 构造函数(终于!)。在GameFactory 中创建(或解决)IPlayerFactoryIGameBoard 是一种不好的做法吗? API.CreateGame 在这种情况下将只有 type 参数?我的意思是对于特定的 GameFactory 只有一个板(每次都一样),所以我不确定我是否需要在一开始就创建板......

编辑: MyGame 构造函数:

public class MyGame : IGame
{
    public IBoard Board { get; }
    public GameType Type { get; }
    public List<IPlayer> Players { get; }

    public MyGame(GameType type, IBoard board, List<IPlayer> players)
    {
        Type = type;
        Board = board;
        Players = players;
    }

    //...
}

【问题讨论】:

  • 您是否应该使用构造函数注入与属性注入或注入与从对象内解析?
  • @DStanley 构造函数注入与从对象内解析。我是否应该在 UI 项目中解析 IGameBoard 并通过构造函数通过 GameAPIGameFactory 将解析的实例注入到 Game 或......在创建 GameFactory 的对象中解析它 MyGame
  • 简短的回答是你应该支持构造函数注入而不是其他模式;特别是Service Locator, which is an anti-pattern。不过,尚不清楚为什么您必须通过层传递board。为什么GameAPI.CreateGameIGameBoard 作为参数?为什么IGameFactory.CreateGame? (为什么GameAPI.GameFactory 是一个可设置的属性?)
  • @MarkSeemann 我知道 Service Locator anit-pattern。这就是我问这个问题的原因。 为什么你必须通过层传递板? - 因为,当我打电话给api.CreateGame 时,我知道它取决于板(它需要板)。我可以跳过它,通过GameFactory.CreateGame 中的new 创建板-但我认为这很糟糕。 为什么 GameAPI.CreateGame 将 IGameBoard 作为参数? - 我需要一个棋盘来创建游戏。不是构造函数注入吗?
  • @MarkSeemann 为什么 GameAPI.GameFactory 是可设置的属性? - 抱歉,这是个错误。我修好了它。 CurrentGame 稍后在课程中用于:CurrentGame.MakeMove 等。

标签: c# .net dependency-injection inversion-of-control ioc-container


【解决方案1】:

我真的需要在程序的最“前端”解析 IGameBoard 吗?

是的,需要在应用程序的入口点和生命周期范围(如果有)的开始处解决依赖关系。通常,这是抽象的(就像在 ASP.NET MVC 中一样),因此您永远不必自己调用 kernel.Get&lt;&gt;(或任何其他 container.Resolve&lt;&gt;)。在 WinForms 中没有这样的机制,所以你必须自己解决依赖关系,最好只有这样的根依赖:

public static class Program
{
    private static void Main()
    {
        var kernel = new StandardKernel();
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(kernel.Get<Form1>());
    }
}

通过在您的主窗体上放置正确的依赖项(可能在您的游戏案例中包括一个工厂),您的应用程序中应该不再需要kernel.Get&lt;&gt;

【讨论】:

    猜你喜欢
    • 2011-01-11
    • 1970-01-01
    • 2011-01-12
    • 1970-01-01
    • 1970-01-01
    • 2017-12-22
    • 2011-03-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多