在保持服务使用明确的同时减少构造函数参数?

Reduce constructor arguments whilst keeping service usage explicit?

我正在使用 C#,但我的问题适用于任何 OOP 语言。

我有许多使用一系列服务的不同对象。我希望访问这些服务的方式能够满足一些限制,但我正在努力寻找一个好的设计。

我认为这些都是很好的目标,但在实践中却很难实现。我目前的方法是一种手动依赖注入

class MyObject
{
    // ... 

    public MyObject(IFooService fooService, IBarService barService)
    {
        this.fooService = fooService;
        this.barService = barService;
    }
}

到目前为止一切顺利。但是当我有很多服务依赖项时会发生什么?我有几十个对象,其中一些需要访问许多服务。

我考虑构建一个 "service container" 对象并将其传入:

class Services
{
    // ... 

    public IFooService FooService
    {
        get { return this.fooService; } 
    }

    public IBarService BarService
    {
        get { return this.barService; } 
    }

    public Services(IFooService fooService, IBarService barService)
    {
        this.fooService = fooService;
        this.barService = barService;
    }
}

class MyObject
{
    // ...

    public MyObject(Services services)
    {
        this.services = services;
    }
}

这样可以节省打字时间,但会掩盖对象实际使用的服务。给定构造函数,MyObject 可能会使用其中的 any

"I considered building a "service container" object"

就像您已经注意到的那样,这只是将问题转移到其他地方,但并没有真正解决它。

有一个类似的设计模式,通常称为 "Parameter Object"…但我认为它并不真正适用于此。根据上一句中引用的文章,它仅适用于您拥有...

"…a particular group of parameters that tend to be passed together."

因此,参数对象模式仅适用于您有多个 class 都依赖于相同服务组件组合的情况(如果您的构造函数参数列表非常长,则这种情况不太可能发生) .

"What happens when I have lots of service dependencies?"

如果您有太多的服务依赖性以至于您的构造函数参数列表变得笨拙,那么也许您的class有太多的责任,即试图做太多很多事情,你应该把它分成几个更小的 classes.

我不太记得是在哪里读到的,我并不是说我完全同意,但一些聪明人曾经声称任何对象不应该有两个以上的字段/对其他对象的引用。这只是为了表明有些人非常重视 "single responsibility principle" 并走极端(恕我直言)来实现这一目标。

这可能表明存在 2 个问题

  1. 要注入的服务粒度太细(例如,如果您有一个创建 Foo 的服务,另一个服务更新 Foo,第三个服务删除 Foo),导致许多依赖项.
  2. 或者,更可能的是,使用该服务的对象粒度太粗,职责太多。例如,如果一半方法使用一组服务,另一半使用另一组服务,则可能很好地表明该对象可以拆分为两个 classes.

总之,如果你觉得你的class不是太复杂,你可以阅读、维护和测试它,我不会太在意依赖的数量。我更喜欢拥有一组清晰、明确的依赖关系,而不是依赖充当服务工厂并依赖于一切的上帝对象。这会损害可测试性和可读性。

Mark Seemann 用解决方案 here 描述了这个问题。

总结:

  1. 确保你没有破坏 SRP
  2. 使用聚合服务重构依赖关系 - Facade 服务

它会在您的代码中引入另一个抽象,但提取的行为很可能会描述域概念,无论如何应该 将它们分开。