当前位置: 首页 > 知识库问答 >
问题:

构造函数注入,避免非依赖参数

平元明
2023-03-14

我需要重构现有的抽象类来实现依赖注入,但是这个类有两个接受其他参数的构造函数。

public abstract class MyClassBase
{
    MyClassBase(int settingId)
    {
        _settingId = settingId;
    }

    MyClassBase(Setting setting)
    {
        _setting = setting;
    }
    ...
}

我需要注入一些接口,并避免在构造函数中传递任何其他参数,如“settingsId”和“Setting”。所以我的想法是在创建此类的实例后创建两种方法来设置这些参数。

public abstract class MyClassBase
{
    private readonly IOneService _oneService;
    private readonly ITwoService _twoService;

    MyClassBase(IOneService oneService, ITwoService twoService)
    {
        _oneService = oneService;
        _twoService = twoService;
    }

    protected void SetupSetting(int settingId)
    {
        _settingId = settingId;
    }

    protected void SetupSetting(Setting setting)
    {
        _setting = setting;
    }

    protected Setting Setting
    {
        get 
        {
            if(_setting == null)
            {
                _setting = _oneService.getSettingById(_settingId);
            }

            return _setting;
        }
    }  
}

但它看起来不是一个合适的解决方案,因为如果开发人员忘记在创建实例后运行这些方法之一,我们将来可能会得到一个异常(对象未设置为引用...)。我应该如何正确执行此操作?

共有2个答案

仉明知
2023-03-14

不。您应该从依赖接口获取所有需要的数据。比如:

MyClassBase(IOneService oneService, ITwoService twoService, ISettings set)
{
    _setting = set;
    ....
}
督德泽
2023-03-14

你的injectables应该只有一个构造函数。拥有多个构造函数是一种反模式。该类需要运行的所有内容都必须通过构造函数传入;使用多个步骤构建对象会导致时间耦合,这是一种设计味道。

所以总的来说,我同意@realnero的解决方案,尽管用接口抽象Settings对象可能没有什么用处。接口旨在抽象行为,但此类设置对象通常只包含数据,而不包含行为。因此,添加抽象将不起作用。

尽管注入 Settings 对象可能很好,但此类对象经常被误用。您不应该传入构造函数,而不是此类直接需要本身。如果 Settings 对象包含整个应用程序的值,则此类需要哪些值将变得不清楚。它还会导致您在每次配置更改时更改 Settings 对象,并且将有许多使用者依赖于这个不断增长的 Settings 类。

这些设置类通常用于调度到配置文件。换句话说,当使用者从 Settings 类请求值时,它会查询配置文件。这将导致配置文件被延迟读取。这使得应用程序在运行时很容易失败,因为配置文件中的拼写错误。相反,您宁愿希望应用程序在启动时直接失败(快速失败)。

因此,您应该只注入此类组件所需的配置值,并且应该在应用程序启动时从配置文件中读取这些值。这允许应用程序在配置不正确时快速失败,非常清楚组件需要什么,并防止您拥有整个应用程序所依赖的中央配置类。

但是拥有具有依赖关系的基类本身就是一种代码气味。没有具体的例子,就不可能说如何重构,但是我认为有两个设计错误导致了基类的使用。

开发人员经常将横切关注点移动到基类中。这将导致这些基类增长并变得难以维护,并导致基类的复杂性与派生类交织在一起,使它们变得复杂且难以测试。相反,应该使用装饰或拦截来添加横切关注点。装饰器设计模式在应用横切关注点时非常有效。它允许服务根本没有基类,并在添加新的横切关注点时提供了很大的灵活性。例如,看看这篇文章,了解一些关于这个的想法。

如果基类不处理横切问题,而是处理许多派生类重用的中央应用程序逻辑,那么将基类逻辑抽象为单独的服务通常是更好的方法。在执行此操作时,可以将此新服务注入到组件中,这允许组件忽略此新服务本身的依赖关系。它可以避免您必须传入基类的依赖项并调用基构造函数。其中一种方法是使用聚合服务。

 类似资料:
  • 问题内容: 我发现我的构造函数开始看起来像这样: 不断增加的参数列表。由于“容器”是我的依赖项注入容器,所以为什么我不能这样做: 每堂课?不利之处是什么?如果执行此操作,则感觉就像我在使用精美的静态方法。请分享你对IoC和依赖注入疯狂的想法。 问题答案: 正确的是,如果将容器用作服务定位器,则它或多或少是光荣的静态工厂。由于种种原因,我认为这是一种反模式。 构造函数注入的奇妙好处之一是,它使违反单

  • 在之前的所有JUnit版本中,测试构造函数或方法都不允许有参数(至少不能使用标准的Runner实现)。作为JUnit Jupiter的主要变化之一,测试构造函数和方法现在都允许有参数。这带来了更大的灵活性,并为构造函数和方法启用依赖注入。 ParameterResolver定义了测试扩展的API,希望在运行时动态解析参数。如果测试构造函数或@Test, @TestFactory, @BeforeE

  • 我正试图在计算机上实现这个例子https://developer.android.com/jetpack/docs/guide.这就解释了Android应用程序的结构。 当我使用相同的代码时,我会出现以下错误。 我可以找出这个错误与 当我为ViewModel编写默认的零输入构造函数时,我得到了以下错误。 我想不出这个错误的原因和解决方法。

  • 好的,到目前为止还好。但是等等,突然A需要额外的输入,比如一个对它的构造至关重要的名为“amount”的整数。现在,我的A构造函数需要如下所示: 突然这个新参数干扰了注射。此外,即使这确实有效,我也无法在从提供者检索新实例时输入“金额”,除非我弄错了。这里有几件事我可以做,我的问题是哪一件是最好的? 我可以通过添加一个方法来重构A,该方法应该在构造函数之后调用。然而,这是很难看的,因为它迫使我推迟

  • 和另一个类: 在这种情况下,服务将按照预期注入到A类中。据我所知,你不能在a类中使用构造函数,否则注入将无法工作。有没有一种方法可以在a类中使用一个构造函数,同时注入服务?即: 在这种情况下,我得到的服务是空的,无论如何要解决这个问题?我希望能够从类型A中使用“new”创建一个对象,并注入服务,这可能吗?

  • 我喜欢在构造函数中传递运行时依赖项的主要原因是: 它使依赖关系成为必需的 它提供了设置实例变量的中心位置 将依赖项设置为实例变量,可以避免在类中从一个方法传递到另一个方法,或者将它分两次或多次传递给两个或更多个公共方法 这导致我在使用Guice时使用了很多辅助注射。与不使用DI相比,这会产生额外的代码,因此阅读以下内容:辅助注入到底有多有用? 似乎大多数人不会使用辅助注入在构造函数中传递运行时(派