通过构造函数在IoC中实现依赖关系?

时间:2009-04-16 15:00:30

标签: c# interface dependency-injection inversion-of-control abstract-class

我正在尝试使用IoC /依赖注入,同时编程为契约而不是特定的类。我所面临的两难境地是:

之间的紧张关系
  1. 为IoC接口编程:我开始时IoC严重依赖接口。从Spring的示例项目来看,接口是在编写与IoC签订合同时的方法。

  2. ...虽然通常首选抽象类the main drawback of interfaces is that they are much less flexible than classes when it comes to allowing for evolution of APIs

  3. 通过构造函数使类依赖项显式化 我的直觉是,将依赖项传递给类的构造函数是一种很好的编程习惯。实际上,这个依赖注入。

  4. ...除非您不能在接口/抽象语句中强制执行构造函数签名:接口或抽象类都不允许定义构造函数签名( easily / elegantly )。 另请参阅Framework Design Guidelines section 4.4: 不要以抽象类型定义公共或受保护的内部构造函数。 ...仅当用户需要创建该类型的实例时,构造函数才应公开。

  5. 此问题与之前的stackoverflow问题有关:Interface defining a constructor signature?

    但我的问题是:

    由于您无法在C#接口/抽象类中定义构造函数,正如上面的问题所要求的那样,在实际层面上:

    您如何将此与passing dependencies in via a constructor 的合理做法相协调?

    编辑:谢谢你的回答。我希望在这种情况下能够了解我应该做什么。只是不使用contructor args?使用某种确实采用依赖关系的Init()方法? 编辑2:谢谢你的答案,非常有帮助。

3 个答案:

答案 0 :(得分:8)

我一直认为使用(制作)示例更容易解释......

想象一下,您有一个ICustomerRepository接口,一个IShoppingCartRepository接口和一个ICheckout接口。您具有这些接口的具体实现 - CustomerRepository,ShoppingCartRepository和CheckoutService。

您的CheckoutService具体类有一个构造函数,它接受一个I​​CustomerRepository和一个IShoppingCartRepository - 例如。

public CheckoutService(ICustomerRepository customerRepository, IShoppingCartRepository shoppingCartRepository)
{
  // Set fields for use in some methods later...
  _customerRepository = customerRepository;
  _shoppingCartRepository = shoppingCartRepository;
}

然后,当你想要一个ICheckoutService实现做一些工作时,你告诉你的IoC容器它应该为每个接口类型使用哪个具体类,并要求它为你构建一个ICheckoutService。您的IoC容器将为您构建您的类,将正确的具体类注入CheckoutService的构造函数中。它也会在这里继续在类heirarchy中构建依赖关系,所以如果你的ShoppingCartRepository在构造函数中使用了一个IDatabaseSession接口,你的IoC容器也会注入该依赖,只要你告诉它使用哪个具体的类。为您的IDatabaseService。

以下是配置(例如)StructureMap作为IoC容器时可能会使用的一些代码(通常在应用启动期间调用此代码):

public class AppRegistry : Registry
{
  public AppRegistry()
  {
    ForRequestedType<ICheckoutService>().TheDefaultIsConcreteType<CheckoutService>();
    ForRequestedType<ICustomerRepository>().TheDefaultIsConcreteType<CustomerRepository>();
    // etc...
  }
}

然后,为了获得ICheckoutService的一个实例,并准备好了,将所有依赖项传递给构造函数,你可以使用类似的东西:

var checkoutService = ObjectFactory.GetInstance<ICheckoutService>();

我希望这是有道理的!

答案 1 :(得分:1)

您的IoC容器必须从具体类型构造一个对象,即使您传递的是一个接口。您的构造函数不是行为或州合同,因此它不属于接口或抽象类的公共成员。

构造函数是一个实现细节,因此您无需将其定义与具体类分开。

答案 2 :(得分:0)

您无法在接口中定义构造函数签名。这无论如何都没有意义,因为接口不应该强制实现如何构造。

抽象类虽然确实可以有构造函数。他们必须得到保护,因为公共建设者也没有意义。它们只能由具体的子类调用。

IoC原则规定,不要让A类知道并实例化B类,而应该将对IB的引用传递给A的构造函数。然后A不需要知道B类,因此您可以轻松地将B类替换为IB的其他实现。

由于您传入的是已经实例化的B类对象,因此IB接口不需要具有构造函数签名。