使用IoC容器的OOD - 如何构建依赖对象?

5

我希望通过IoC、DI和OOD技术的学习,提高测试性和松散耦合度。

当我们使用大量的IoC和DI设计类时,例如多个依赖项的类,我们可以使其更具可测试性。

class Processor
{
    private IService1 m_Service1;
    private IService2 m_Service2;
    private IService3 m_Service3;

    //approach 1 
    public Processor(IService1 service1, IService2 service2, IService3 service3)
    {
        m_Service1 = service1;
        m_Service2 = service2;
        m_Service3 = service3;
    }
    //approach 2
    public Processor(IContainer container)
    {
        m_Service1 = container.Resolve<IService1>();
        m_Service2 = container.Resolve<IService2>();
        m_Service3 = container.Resolve<IService3>();
    }

    //approach 3
    public delegate Processor Factory();

}

我在思考这里应该采取什么常规方法。我们可以保留具有3个参数的构造函数,但如果我们使用autofac(例如)构建应用程序,则很可能仅在通过解析某些容器实例的类型时才会使用它。

    Processor proc = new Processor(
 container.Resolve<IService1>(),
 container.Resolve<IService2>(),
 container.Resolve<IService3>());

所以我认为,也许方法2更好,当我们从容器中依赖多个类型时。无论如何,我们都必须在某个地方添加对autofac的引用,那么现在没有理由不这样做吧?

Autofac还提供了委托工厂方法的方式。

http://code.google.com/p/autofac/wiki/DelegateFactories

var processorFactory = container.Resolve<Processor.Factory>();
Processor processor = processorFactory.Invoke();

我们还有第三种方法-不使用构造函数来创建类实例,而是从容器中调用已解决的委托,并且容器将为我们解决依赖关系。
由于我对IoC相当新,很难说我们应该何时使用1、2、3。它们都有优点和缺点。
我认为通常如果类只有一个依赖项,我们可能总是可以使用方法1... 除此之外,我真的不知道该选择什么和何时选择。
更新:我已经阅读了关于服务定位器反模式的信息,但我提出了第四种(或真正的第三种)方法。
它与ServiceLocator接近,但不同之处在于,我们传递一个看起来像这样的对象。
public class ServiceLocatorObject
{
        private IService1 m_Service1;
        private IService2 m_Service2;
        private IService3 m_Service3;
        public IService1 Service1 {get {return m_Service1;}}
        public IService2 Service2 {get {return m_Service2;}}
        public IService3 Service3 {get {return m_Service3;}}

        public ServiceLocatorObject(IService1 service1, IService2 service2, IService3 service3)
        {
            m_Service1 = service1;
            m_Service2 = service2;
            m_Service3 = service3;
        }
}

现在我们要创建:
//approach 4
public Processor(ServiceLocatorObject servicesToUse)
{
    m_Services = servicesToUse;
}

现在我们已经将类与服务实现解耦,并且明确了它需要的真正依赖关系(如果我们假设传递对象上所有可用的服务都是必需的),因为我们不会传递可以包含100个实现的容器。如果该对象可能在应用程序中的另一个类中需要这3个服务组合,那么该对象甚至可以被重用。因此,我们使用构造函数DI而不是ServiceLocator模式。接口清晰而没有超载依赖项,新的类可能是一个很好的重用候选项。
3个回答

6
JeffN825的回答是正确的,但我要补充一点,你永远不会使用像这样的容器创建一个新的Processor实例:
Processor proc = new Processor(
    container.Resolve<IService1>(),
    container.Resolve<IService2>(),
    container.Resolve<IService3>());

相反,您会让容器 自动连接 依赖项并进行一次性解析
Processor proc = container.Resolve<Processor>();

谢谢,马克。你能否谈一下第三种方法?在什么情况下它比你建议的 Processor proc = container.Resolve<Processor>(); 或第一种方法更好? - Valentin Kuzub
1
第三种方法只是一个伪装成抽象工厂的东西:http://blog.ploeh.dk/2009/05/28/DelegatesAreAnonymousInterfaces.aspx 它与另外两种方法没有真正的关联。你会怎么处理它? - Mark Seemann
嗨,马克,我在阅读了你的一些帖子后,对问题进行了更新,你觉得怎么样? - Valentin Kuzub
ServiceLocatorObject 是做什么的?我无法从提供的示例中确定,因为它没有任何行为? - Mark Seemann
1
从 DI 的角度来看,这是可以接受的。从一般的角度来看,这是一个参数对象。如果它们将相关概念分组在一起,那么它们就很好,但如果仅在机械意义上使用,则不行。因此,我无法对这种方法的有效性给出任何一般答案,因为它取决于上下文... 与参数对象密切相关的还有外观服务(以前称为聚合服务):http://blog.ploeh.dk/2010/02/02/RefactoringToAggregateServices.aspx - Mark Seemann
显示剩余5条评论

6
服务定位模式现在通常被认为是一种反模式(使用container.Resolve并注入容器)。
在我自己与这个概念苦苦挣扎并试图决定我喜欢它还是讨厌它之后,我个人意识到我同意服务定位是一个反模式——因为它混淆了存在的相互依赖关系,而这些关系是面向对象编程的核心概念之一。
请看这里: http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx 实际上,我喜欢选项1中的事实,即Process在构造函数中列出每个服务作为参数明确表达了其对它们的依赖性。这使得依赖关系非常明显......而且我认为它有助于促进良好的设计。
仅仅说Processor需要一个IContainer并不能告诉我们太多......因此你需要仔细查看以确定相互依赖关系。

链接很好,那本书看起来正是我需要的。你对第三种方法有什么看法? - Valentin Kuzub
1
我在我的代码中使用第三种方法...当我需要注入一种方式来随时间创建许多类型的实例时。我使用Windsor...所以我经常使用委托工厂,并在我知道我的处理器将需要随时间创建许多IService1时注入Func<IService1>... - Jeff

0

这不是关于依赖数量或每个类的决定。方法2引入了一个新的依赖,但如果您想依赖于IoC容器,那么这是一个好方法。方法3与第二种方法类似,但允许您在将来的工厂中执行一些操作。方法1最简单,不依赖于任何东西,并且应该用于通常不通过IoC容器管理的依赖项。


这不是关于依赖数量或每个类的决定。- 你能详细说明一下吗?所以我们应该为整个应用程序选择单一方法并在所有地方使用它,还是你在说什么?我不确定为什么不能是每个类的决定。 - Valentin Kuzub
使用IoC容器的选择会影响整个应用程序 - 每个模块都应该依赖于它。方法1是自然的 - 所有开发人员都能理解。方法2是特定于容器的。最糟糕的事情是混合使用它们。代码的读者必须检查使用了哪种方法,编写测试的作者也是如此。 - Gabriel Ščerbák

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接