具有静态和动态依赖关系的IoC

本文关键字:关系 IoC 依赖 动态 静态 | 更新日期: 2023-09-27 18:11:23

我想在我的应用程序中实现IoC。我有这个模型:

interface IService;
interface IComponent;
class Service : IService
    Service()
class Component : IComponent
    Component(IService service, object runtimeValue) { }

在我的应用程序的某个点,我需要得到一个IComponent。我的应用程序使用IoC容器(Unity)。我可以用容器注册Service,但我不能为Component b/c的依赖runtimeValue做同样的事情。根据这一点,我必须使用一个工厂,并注入,无论我需要得到一个IComponent:

interface IComponentFactory
     IComponent CreateComponent(object runtimeValue)
class ComponentProvider : IComponentProvider
     ComponentProvider(IComponentFactory factory) { }
     IComponent CreateAndCacheComponent(object runtimeValue) {
         _component = factory.CreateComponent(runtimeValue)
         return _component
     }
     // other methods

我必须能够向容器注册工厂,所以它必须只有静态依赖项。同时,它必须能够提供创建组件所需的IService类型的服务实例。下面是工厂实现。我唯一能想到的就是使用Func<>委托作为依赖项:

class ComponentFactory : IComponentFactory
    ComponentFactory(Func<IService> serviceFactoryDelegate)
    IComponent CreateComponent(object runtimeValue) {
        return new Component(serviceFactoryDelegate.Invoke(), runtimeValue)
    }

…并将委托注册为容器的静态工厂,以便它回调容器来解析服务(我在。net 2.0上使用Unity 1.2):

Container
    .Configure<IStaticFactoryConfiguration>()
    .RegisterFactory<Func<IService>>(container => (Func<IService>)container.Resolve<IService>)

现在我可以使用容器来解析ComponentProvider并获得基于运行时值的组件:

// this happens inside CompositionRoot
provider = Container.Resovle<IComponentProvider>()
component = provider.CreateAndCacheComponent("the component")

现在我有一些问题:

  1. 我不高兴工厂打电话给new Component(...)。这不是穷人的DI吗?

  2. 当在工厂的构造器上使用Func<IService>时,好莱坞原则仍然成立吗?我的意思是,它最终调用container.Resolve<>…有点像SL,唯一的区别是代码在应用程序的容器注册部分,而不是在工厂类中。

  3. 这个实现有什么(其他)错误吗,就DI和IoC而言?

具有静态和动态依赖关系的IoC

  1. 这是远离穷人的DI的一大步,但如果你不必在每次向组件的构造函数中添加新的依赖时都更改这个工厂方法,那就更好了。
  2. 这不是本身的问题。可以把它看作是在注入一个匿名工厂类。它仍然可以用于单元测试,并且可以更改绑定,因此您仍然可以获得DI的好处。但它是一个额外的抽象层,这可能是不必要的。在这种情况下,您仍然可以通过将IService直接注入工厂而不是Func来避免它。
  3. 通常在使用依赖注入时,你希望注入服务而不是。如果您发现两者都必须具备,这可能表明您需要重新考虑类的API。例如,也许应该将值传递给类上的方法,而不是传递给构造函数。在不了解更多细节的情况下,很难说什么是最好的方法。
    不,它不是。工厂的全部目的就是创建一个具体类的实例。基本上,是的,但正如我在评论中已经问过的,我不明白为什么这是必要的。你可以直接注入IService的实例
  1. 这比它需要的要复杂一点。为什么双重定向是IComponentProvider -> IComponentFactory ?看起来IComponentFactory并没有增加任何好处。

    这样实现ComponentProvider:

    class ComponentProvider : IComponentProvider
    {
        ComponentProvider(IService service) { _service = service; }
        IComponent CreateAndCacheComponent(object runtimeValue) {
            _component = new Component(_service, runtimeValue);
            return _component;
    }
    

    这将给您带来以下好处:

    1. 去掉不必要的接口IComponentFactory以及相应的实现。
    2. 无需为IService注册工厂

一般来说,如何实现它取决于你真正需要什么:

"runtimeValue"在整个运行时可以是相同的,例如,从设置中读取的连接字符串。在这种情况下,不需要工厂/提供者,您可以简单地新建实例并将其注册到容器中。每个需要IComponent的人都在构造函数而不是提供程序中请求一个。

如果"runtimeValue"在调用CreateAndCacheComponent之间真的发生了变化,你只能实现一个工厂,并将其作为依赖项传递。

问题1:在工厂调用new没有问题。您已将实例化隔离到应用程序中的一个位置;你只是把那个地方变成了工厂而不是集装箱。

如果你需要模拟或更改实现,你只需要模拟或更改工厂实现,而不是单独模拟或更改组件。