Enforcing dependencies in IoC via a constructor?
我试图在使用IOC/Dependency注入的同时将编程收缩,而不是特定的类。我面临的困境是:
为国际奥委会做接口编程:我从一开始就非常依赖接口。从Spring的示例项目来看,在编程与IOC签订合同时,接口是可行的方法。
(……尽管抽象类通常更受欢迎:接口的主要缺点是,在允许API进化方面,它们比类要灵活得多)
通过构造函数使类依赖关系显式化我的直觉是,将依赖项传递给类的构造函数是一种很好的编程实践。实际上,这是依赖注入。
…但不能在接口/抽象类中强制构造函数签名:接口或抽象类都不允许定义构造函数签名(容易/优雅)。另请参见框架设计指南第4.4节:不要在抽象类型中定义公共或受保护的内部构造函数。…只有当用户需要创建该类型的实例时,构造函数才应该是公共的。
这个问题与前面的stackoverflow问题有关:定义构造函数签名的接口?
但我的问题是:
因为您不能在C接口/抽象类中定义构造函数,正如上面的问题所要求的那样,在实际级别上:
如何将这与通过构造函数传递依赖项的合理实践协调起来?
编辑:谢谢你的回答。我希望能对我在这件事上应该做些什么有所了解。只是不使用contractor args?使用某种类型的init()方法来获取依赖项?伊迪丝2:谢谢你的回答,非常有帮助。
我一直认为用一个(虚构的)例子来解释这件事比较容易…
假设您有一个icustomerepository接口、一个ishoppingcartpository接口和一个icheckout接口。您有这些接口的具体实现——customerrepository、shoppingcartreository和checkoutservice。
您的checkoutService concrete类有一个构造函数,该构造函数接受ICustomerRepository和IShoppingCartPository,例如
1 2 3 4 5 6 | 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容器时可能使用的一些代码(此代码通常在应用程序启动期间调用):
1 2 3 4 5 6 7 8 9 | public class AppRegistry : Registry { public AppRegistry() { ForRequestedType<ICheckoutService>().TheDefaultIsConcreteType<CheckoutService>(); ForRequestedType<ICustomerRepository>().TheDefaultIsConcreteType<CustomerRepository>(); // etc... } } |
然后,为了获得一个已构建并准备好的ICheckOutService实例,并将所有依赖项传递给您的构造函数,您将使用如下内容:
1 | var checkoutService = ObjectFactory.GetInstance<ICheckoutService>(); |
我希望这是有道理的!
您的IOC容器必须从具体类型构造一个对象,即使您传递的是一个接口。构造函数不是行为或状态协定,因此它不属于接口或抽象类的公共成员。
构造函数是一个实现细节,因此不需要将其定义与具体类分离。
不能在接口中定义构造函数签名。这无论如何都没有意义,因为接口不应该强制实现的构造方式。
不过,抽象类确实可以有构造函数。它们必须受到保护,因为公共构造函数也没有意义。它们只能由具体的子类调用。
IOC原则规定,不要让类A知道并实例化类B,而是应该将对ib的引用传递给a的构造函数。这样,a就不需要知道类B,因此您可以很容易地用一些其他的ib实现来替换类B。
因为您传递的是已经实例化的类B对象,所以ib接口不需要有构造函数签名。