【问题标题】:Enforcing dependencies in IoC via a constructor?通过构造函数在 IoC 中强制执行依赖关系?
【发布时间】:2010-10-19 21:12:47
【问题描述】:

我正在尝试接受使用 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: 不要在抽象类型中定义公共或受保护的内部构造函数。 ...仅当用户需要创建该类型的实例时,构造函数才应该是公共的。

这个问题与之前的stackoverflow问题有关:Interface defining a constructor signature?

但我的问题是:

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

你如何协调这与passing dependencies in via a constructor的明智做法

编辑:感谢您的回答。我希望对我在这种情况下应该做什么有所了解。只是不使用构造函数参数?使用某种具有依赖关系的 Init() 方法? Edit2:感谢您的出色回答,非常有帮助。

【问题讨论】:

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


    【解决方案1】:

    我一直认为用(编造的)例子更容易解释......

    假设您有一个 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>();
    

    我希望这是有道理的!

    【讨论】:

    • 啊哈!所以我可以继续使用构造函数参数来明确依赖关系,同时对合约进行编程。但是让 IoC 容器担心正确地注入这些依赖项。谢谢,这完全澄清了。
    【解决方案2】:

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

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

    IoC 原则规定,与其让 A 类知道并实例化 B 类,不如将对 IB 的引用传递给 A 的构造函数。这样 A 就不需要知道 B 类,因此您可以轻松地将 B 类替换为 IB 的其他一些实现。

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-03-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多