【问题标题】:Object lifecycle management and IoC containers对象生命周期管理和 IoC 容器
【发布时间】:2012-11-06 02:55:35
【问题描述】:

我正在将游戏从单人游戏更新为多人游戏。在这种情况下,游戏最初是在大多数类都是单实例的情况下编写的。例如有一个 Player 对象、一个 GameState 对象等。也就是说,这些对象中的每一个都与应用程序一样长。

既然可以同时玩多个玩家,我显然需要支持创建多个 Player 对象、GameState 对象等。在此过程中,我开始意识到大多数对象都有三个生命周期之一:

  1. 应用程序的生命周期,例如处理导航的导体
  2. 玩家的生命周期,例如当前播放器的 SettingsViewModel
  3. 游戏的生命周期,例如当前游戏的 GameState

我很好奇其他人如何使用 IoC 容器来处理这些不同对象的创建。我想避免为每个具有玩家或游戏生命周期的类创建工厂类。

【问题讨论】:

  • 配置生活方式/生命周期通常是大多数 DI 容器支持的。你说的是哪个平台和 DI 框架?
  • 我知道一些容器在一定程度上支持生命周期管理,但是想象一个将 IPlayer 作为参数的类(就像许多类在这种情况下所做的那样)。如果只有一个容器,那么如何在不需要工厂存储运行时数据的情况下注册此类?我已经尝试为每个生命周期创建单独的容器,但是当关注点跨越生命周期时,我仍在努力连接各个部分。
  • 在这种情况下 .NET/C# 但是我实际上对人们如何更普遍地解决这个设计问题很感兴趣。特别是,我不想过分依赖给定的容器,因为它可能并非在我所针对的每个平台上都可用。
  • 虽然令人钦佩,但不同平台的可能性差异很大。例如,处于静态-动态范围末端的语言(例如 C++ 和 PHP)没有像 .NET 和 Java 那样丰富的类型系统。这极大地限制了 DI 容器的可能性。在当前状态下,Java 端的 DI 容器似乎完全基于基于 XML 的配置,这非常疯狂,因为大的 XML 文件会造成维护的噩梦。我认为 .NET 拥有最好的 DI 容器,这为创建可维护的软件提供了最佳机会。
  • @Steven:请更新您有关 .NET 之外的 IoC 容器的信息。首先,在 2012 年之前,Java 中有很多使用注解的 IoC 容器,而 PHP 可能比您想象的更严格。比较值时使用 === 而不是 ==。

标签: dependency-injection inversion-of-control


【解决方案1】:

我认为这部分是对先前答案的一些 cmets 的重新散列,但我试图举例说明扩展一些推理。

一旦您进入管理注入对象生命周期的领域,您可能应该为这些对象创建工厂。

根本问题是组合根不知道创建对象所需的调用环境上下文是什么。

我想我应该退后一步并在这一点上进行解释。

关于依赖注入的公认智慧是在代码入口点附近有一个组合根。这有很多很好的理由,在网上不难找到,所以我不会在这里讨论。

组合根是您将接口(通常,但也可能是对象)映射到它们的实现的地方。您可以将此时可用的信息传递给构造函数。因此,您可以传入一个对象的引用,该对象的生命周期在执行组合根时是当前的。

但是,如果组合根的生命周期与要创建的对象的生命周期不重叠,则必须将构造函数的执行推迟到需要创建对象时。这就是为什么你需要有一个工厂。此时您可以将工厂方法传递给您的映射,从而传递生成对象所需的信息,但允许在需要时进行创建,而不是在执行组合根时。

你不需要工厂类来做这个工厂方法就可以了,而且工厂方法可以内联,所以代码开销并不比我们在组合路由中创建对象多多少。

如果我们有一个包含 2 个服务的项目,其中第一个服务依赖于第一个服务,并且我们只希望第二个服务的生命周期在我们创建第一个服务时开始,我们可能会得到以下内容。 (我使用ninject来给出一个代码示例,但我希望其他IOC容器在这方面也能类似地工作。)


`
    公共类Service1:IService
    {
        私有 FuncserviceFactoryMethod _Service2Factory;
        公共服务1(Funcservice2FactoryMethod)
        {
            _Service2Factory=service2FactoryMethod;
        }

        公共无效 DoSomethingUsingService2()
        {
            var service2=_Service2Factory();
            service2.DoSomething();
        }
    }

    公共类主类
    {
        公共无效CompositionRoot()
        {
            var kernel= new StandardKernel();
            kernel.Bind.ToMethod(m=>
            {
               return new Service1(m.Kernel.Get());
            }
        }
    }

`

本示例并未说明您将如何管理应用程序的生命周期、玩家和游戏的生命周期,但希望它能够提供足够的线索来说明如何消除与依赖注入相关的生命周期问题。

旁注:使用 Ninject,您将能够更改 Service2 的范围,以便管理其生命周期,使其超出 Service1 的生命周期。例如,如果您知道游戏的每个实例都将在其自己的线程上发生(好吧,这可能不太可能),您可以为游戏使用 InThreadScope。

【讨论】:

    【解决方案2】:

    根据我的经验,工厂方法效果最好。

    控制实例的生命周期很难获得支持,需要努力、了解所有类的生命周期要求和依赖关系、配置时间和配置管理。同时,工厂的使用是自然的并且是特定于代码的。

    使用proxy factories 可以避免创建工厂(实现)。您还可以让工厂返回通用参数,以进一步减少工厂(接口)创建的需求。

    如果仍然需要太多工厂,我建议查看代码流。

    【讨论】:

      【解决方案3】:

      许多 IoC 容器都有自定义的生命周期范围,您可以根据自己的意愿进行管理。例如,在 Ninject 中,您可以按如下方式定义自定义生命周期范围:

      kernel.Bind<IService>().To<Service>().InScope((c, o) => yourCustomeScope);
      

      只要 yourCustomeScope 变量没有改变,每次内核收到对 IService 的请求时,都会返回一个 Service 对象的实例。一旦 yourCustomeScope 变量发生变化,就会在下一次请求 IService 时创建一个新的 Service 实例。 yourCustomeScope 可以是当前玩家实例、游戏对象或任何您希望根据其引用更改更改 Service 对象生命周期的内容。

      但是,您刚才提到的对象更有可能是实体而不是服务,我认为注入不是一个好主意。

      【讨论】:

        【解决方案4】:

        Here 是一个可以提供帮助的 IOC 示例。该项目称为 IOC-with-Ninject。它使用 Ninject 和一个 IOC 容器类来管理所有对象的生命周期。您将需要做一点research on Ninject 来根据您的特定需求对其进行自定义,但如果您使用的是 .NET,这是您的 IOC 容器解决方案 (IMHO),它将帮助您组织代码库。这是个人选择,但我发誓。如果你不使用 .NET,它仍然会给你一个简单的模式来遵循。干杯。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-07-28
          • 1970-01-01
          • 2013-08-08
          • 1970-01-01
          • 2013-04-25
          • 2012-12-28
          • 1970-01-01
          相关资源
          最近更新 更多