7

如果我有一个通过构造函数依赖注入服务的基类:是否可以在不使用的情况下声明子类的构造函数: base (params)

public MyBaseClass
{
   private IServiceA _serviceA;
   private IServiceB _serviceB;
   private IServiceC _serviceC;

   public MyBaseClass(null, null, null)
   public MyBaseClass(IServiceA serviceA, IServiceB serviceB, IServiceC serviceC)
   {
       _serviceA = serviceA;
       _serviceB = serviceB;
       _serviceC = serviceC;
   }
}

还有一个注入了一些额外依赖项的子类:

public MySubClassA : MyBaseClass
{
   private IServiceD _serviceD;

   public MySubClassA (null, null, null, null)
   public MySubClassA (IServiceA serviceA, IServiceB serviceB, 
                       IServiceC serviceC, IServiceD serviceD)
          : base (serviceA, serviceB, serviceC)
   {
       _serviceD = serviceD;
   }
}

这里的问题是我有多个子类,现在只有 10 个左右,但数量会增加。每次我需要向基类添加另一个依赖项时,我都必须遍历每个子类并在那里手动添加依赖项。这个手工工作让我觉得我的设计有问题。

那么是否可以MyBaseClassA在子类的构造函数中声明没有基类所需服务的构造函数呢?例如,这样的构造函数 MyBaseClassA只有这个更简单的代码:

   public MySubClassA (null)
   public MySubClassA (IServiceD serviceD)
   {
       _serviceD = serviceD;
   }

我需要在基类中更改什么,以便在那里发生依赖注入并且不需要将其添加到子类中?我正在使用 LightInject IoC。

4

1 回答 1

17

这个手工工作让我觉得我的设计有问题。

大概有。您给出的示例很难具体说明,但通常此类问题是由以下原因之一引起的:

  • 您使用基类来实现横切关注点(例如日志记录、事务处理、安全检查、验证等)而不是装饰器。
  • 您使用基类来实现共享行为,而不是将该行为放在可以注入的单独组件中。

基类经常被滥用来为实现添加横切关注点。在这种情况下,基类很快就变成了一个上帝对象:一个做的太多,知道的太多的类。它违反了单一职责原则(SRP),导致它经常更改、变得复杂并且难以测试。

该问题的解决方案是一起删除基类并使用多个装饰器而不是单个基类。您应该为每个横切关注点编写一个装饰器。这样每个装饰器都小而专注,并且只有一个改变的理由(单一职责)。通过使用基于通用接口的设计,您可以创建可以包装一整套与架构相关的类型的通用装饰器。例如,一个可以包装系统中所有用例实现的单个装饰器,或者一个包装系统中具有特定返回类型的所有查询的装饰器。

基类经常被滥用以包含一组被多个实现重用的不相关功能。与其将所有这些逻辑放在基类中(使基类变成维护噩梦),不如将此功能提取到实现可以依赖的多个服务中。

当您开始针对此类设计进行重构时,您会经常看到实现开始获得许多依赖项,这是一种称为构造函数过度注入的反模式。构造函数过度注入通常表明一个类违反了 SRP(使其复杂且难以测试)。但是将逻辑从基类转移到依赖项并没有使实现更难测试,实际上具有基类的模型也存在相同的问题,但不同之处在于依赖项被隐藏起来了。

仔细查看实现中的代码时,您经常会看到某种重复出现的代码模式。多个实现以相同的方式使用同一组依赖项。这是缺少抽象的信号。可以将此代码及其依赖项提取到聚合服务中。聚合服务减少了实现所需的依赖数量,并包装了常见行为。

使用聚合服务看起来像 @SimonWhitehead 在评论中谈到的“包装器”,但请注意聚合服务是关于抽象依赖关系和行为的。如果您创建依赖项的“容器”并通过公共属性公开这些依赖项以供实现使用它们,那么您不会降低实现所依赖的依赖项的数量,也不会降低此类的复杂性,并且不会该实现更易于测试。另一方面,聚合服务确实降低了依赖项的数量和类的复杂性,使其更容易掌握和测试。

遵循这些规则时,在大多数情况下不需要基类。

于 2013-07-01T11:39:20.240 回答