在保持服务使用明确的同时减少构造函数参数?
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 个问题
- 要注入的服务粒度太细(例如,如果您有一个创建 Foo 的服务,另一个服务更新 Foo,第三个服务删除 Foo),导致许多依赖项.
- 或者,更可能的是,使用该服务的对象粒度太粗,职责太多。例如,如果一半方法使用一组服务,另一半使用另一组服务,则可能很好地表明该对象可以拆分为两个 classes.
总之,如果你觉得你的class不是太复杂,你可以阅读、维护和测试它,我不会太在意依赖的数量。我更喜欢拥有一组清晰、明确的依赖关系,而不是依赖充当服务工厂并依赖于一切的上帝对象。这会损害可测试性和可读性。
Mark Seemann 用解决方案 here 描述了这个问题。
总结:
- 确保你没有破坏 SRP
- 使用聚合服务重构依赖关系 - Facade 服务
它会在您的代码中引入另一个抽象,但提取的行为很可能会描述域概念,无论如何应该 将它们分开。
我正在使用 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 个问题
- 要注入的服务粒度太细(例如,如果您有一个创建 Foo 的服务,另一个服务更新 Foo,第三个服务删除 Foo),导致许多依赖项.
- 或者,更可能的是,使用该服务的对象粒度太粗,职责太多。例如,如果一半方法使用一组服务,另一半使用另一组服务,则可能很好地表明该对象可以拆分为两个 classes.
总之,如果你觉得你的class不是太复杂,你可以阅读、维护和测试它,我不会太在意依赖的数量。我更喜欢拥有一组清晰、明确的依赖关系,而不是依赖充当服务工厂并依赖于一切的上帝对象。这会损害可测试性和可读性。
Mark Seemann 用解决方案 here 描述了这个问题。
总结:
- 确保你没有破坏 SRP
- 使用聚合服务重构依赖关系 - Facade 服务
它会在您的代码中引入另一个抽象,但提取的行为很可能会描述域概念,无论如何应该 将它们分开。