通过构造函数强制 IoC 中的依赖关系?

发布于 2024-07-17 08:34:11 字数 1404 浏览 5 评论 0原文

我试图接受使用 IoC/依赖注入,同时对契约而不是特定的类进行编程。 我面临的困境是以下之间的紧张关系:

  1. 为 IoC 接口编程:我一开始就严重依赖接口的 IoC。 从 Spring 的示例项目来看,接口是根据 IoC 合约进行编程时的最佳选择。

  2. ( ...虽然通常首选抽象类接口的主要缺点是,在允许 API 发展方面,它们比类灵活性差得多

  3. 通过构造函数明确类依赖关系 我的直觉是,将依赖项传递给类的构造函数是很好的编程实践。 事实上,这依赖注入。

  4. ...除非你不能在接口/抽象类中强制执行构造函数签名:接口或抽象类都不允许定义构造函数签名(轻松/优雅)。 另请参阅框架设计指南第 4.4 节: 不要< /strong> 在抽象类型中定义公共或受保护的内部构造函数。 ...只有当用户需要创建该类型的实例时,构造函数才应该是公共的。

这个问题与之前的一个 stackoverflow 问题相关: Interfacedefiningaconstructor-signature?

但是我的问题是:

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

您如何将其与 通过构造函数传递依赖项

编辑:谢谢您的回答。 我希望能够了解在这种情况下我应该做什么。 只是不使用构造函数参数吗? 使用某种接受依赖项的 Init() 方法? Edit2:感谢您的精彩回答,非常有帮助。

I'm trying to come to terms with using IoC/Dependency Injection while at the same time programming to contracts rather than specific classes. The dilemma I'm having is the tension between:

  1. Do program to interfaces for IoC: I started out with IoC relying heavily on interfaces. Judging by Spring's sample projects, interfaces are the way to go when programing to a contract with IoC.

  2. ( ... although abstract classes generally preferred: the main drawback of interfaces is that they are much less flexible than classes when it comes to allowing for evolution of APIs )

  3. Do make class dependencies explicit via constructor
    My gut feeling is that it's good programming practice to pass dependencies in to a class's constructor. Indeed, this is dependency injection.

  4. ... except you can't enforce constructor signature in interfaces/abstract clases: Neither interfaces or nor abstract classes allow for defining a constructor signature ( easily / elegantly ).
    See also Framework Design Guidelines section 4.4: DO NOT define public or protected internal constructors in abstract types. ... Constructors should be public only if users will need to create instances of the type.

This question is related to a previous stackoverflow question: Interface defining a constructor signature?

But my question is:

Since you can't define a constructor in a C# interface/abstract class, as the question above asks, on a practical level:

How do you reconcile this with the sensible practice of passing dependencies in via a constructor?

Edit: Thank you for the answers. I'm hoping for some insight on what I should do in this case. Just not use contructor args? Use some sort of Init() method that does take the dependencies?
Edit2: Thanks for the great answers, very helpful.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

甜警司 2024-07-24 08:34:12

我始终认为用(虚构的)示例更容易解释这一点...

假设您有一个 ICustomerRepository 接口、一个 IShoppingCartRepository 接口和一个 ICheckout 接口。 您拥有这些接口的具体实现 - CustomerRepository、ShoppingCartRepository 和 CheckoutService。

您的 CheckoutService 具体类有一个构造函数,该构造函数采用 ICustomerRepository 和 IShoppingCartRepository - 例如,

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

然后,当您希望 ICheckoutService 实现执行某些操作时,您告诉 IoC 容器它应该为每个接口类型使用哪个具体类并要求它构建你是一个 ICheckoutService。 您的 IoC 容器将为您构建类,将正确的具体类注入到 CheckoutService 的构造函数中。 它还会在此处沿着类层次结构一路构建依赖关系,因此,例如,如果您的 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>();

我希望这是有道理的!

I always think this is easier to explain with a (made up) example...

Imagine you have an ICustomerRepository interface, an IShoppingCartRepository interface and an ICheckout interface. You have concrete implementations of those interfaces - CustomerRepository, ShoppingCartRepository, and CheckoutService.

Your CheckoutService concrete class has a constructor that takes an ICustomerRepository and an IShoppingCartRepository - e.g.

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

Then, when you want an ICheckoutService implementation to do some work with, you tell your IoC container which concrete class it should use for each interface type and ask it to build you an ICheckoutService. Your IoC container will go and build your classes for you, injecting the correct concrete classes into the constructor of your CheckoutService. It will also build dependencies all the way down the class heirarchy here, so if, for example your ShoppingCartRepository takes an IDatabaseSession interface in the constructor, your IoC container will inject that dependency too, as long as you have told it which concrete class to use for your IDatabaseService.

Here's some code you might use when configuring (for example) StructureMap as your IoC container (this code would typically be called during app startup):

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

Then to get an instance of ICheckoutService built up and ready to go, with all the dependencies passed into the constructor for you, you would use something like:

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

I hope that makes sense!

方觉久 2024-07-24 08:34:12

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

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

Your IoC container must construct an object from a concrete type, even though what you're passing around is an interface. Your constructor is not a behavior or state contract, so it does not belong in an interface or as a public member of your abstract class.

A constructor is an implementation detail, so you do not need to separate its definition from the concrete class.

倾城°AllureLove 2024-07-24 08:34:12

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

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

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

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

You cannot define constructor signatures in interfaces. That wouldn't make sense anyway since the interface shouldn't enforce how the implementations are constructed.

Abstract classes though can indeed have constructors. They must be protected since public constructors does not make sense either. They should only be called by concrete subclasses.

The IoC principle dictates that instead of having class A know about and instantiate class B, you should instead pass in a reference to IB to the constructor of A. Then A will not need to know about class B and thus you can easily substitute class B with some other implementation of IB.

Since you're passing in an already instantiated object of class B, the IB interface doesn't need to have a constructor signature.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文