可配置的依赖关系,易于模拟默认实现 [英] configurable dependencies with easy to mock out default implementations

查看:44
本文介绍了可配置的依赖关系,易于模拟默认实现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在研究一种参数值解析器库.我想定义一个解析器,如下所示:

I'm working on a kind of parameter values parser library. I'd like to have an Parser defined as follows:

public class Parser {

    private ValuesConfiguration configuration;
    private ValuesProvider valuesProvider;
    private ValuesMapper valuesMapper;

    public Parser(ValuesConfiguration configuration) {
        this.configuration = configuration;
    }

    public Result parse(String parameterName) {
        List<Values> values = valuesProvider.getValues(parameterName);
        // do other stuff on values
        // ...
        return valuesMapper.transformValues(values, configuration);
    }
}

我希望该库客户端不了解ValuesProvider和ValuesMapper的默认实现,并像这样使用它

I'd like this library clients to be unaware of ValuesProvider and ValuesMapper default implementations and use it like

Result result = new Parser(myConfig).parse("sampleParam");

尽管必须在需要时设置自己的实现.我想知道如何以及在何处初始化这些默认实现,并且仍然允许客户端根据需要设置自己的默认实现.我不想坚持

Although there must be possibility to set their own implementations when needed. I wonder how and where should I init those default implementations and still let clients to set their own if they want. I don't want to stick to

new DefaultValuesProvider()

等在构造函数中,因为默认实现例如访问文件系统,因此很难进行测试(将其模拟).我知道我可以使用设置器(例如DI中的设置器),但是默认设置如何呢?

etc. in constructor, because default implementation would e.g. access filesystem, so that it would be hard to test (mock them out). I know I can use setters (like in DI) but what about defaults?

回答完所有问题后,我认为最好是在这里设置设置器,以允许客户提供自己的ValuesProvider和ValuesMapper实现.但是如何创建默认实现?我想将实例化与逻辑分离,所以我不想在这里使用新的DefaultValueProvider().工厂模式在这里适用吗?如果是这样,我应该在哪里使用它?

After your all answer, I guess it is best here to have setters to allow clients to provider their own implementations of ValuesProvider and ValuesMapper. But how to create default implementations? I'd like to decouple instantiation from logic, so I don't want to use new DefaultValueProvider() here. Is the factory pattern applicable here? If so how and where should I use it?

推荐答案

如何:

public void setValuesProvider(ValuesProvider valuesProvider) {
    this.valuesProvider = valuesProvider;
}

public Result parse(String parameterName) {
    if (valuesProvider == null) {
        valuesProvider = new DefaultValuesProvider();
    }

    List<Values> values = valuesProvider.getValues(parameterName);
    // do other stuff on values
    // ...
    return valuesMapper.transformValues(values, configuration);
}

正如Mark所指出的,构造函数注入可能是更好的方法.如下所示:

As Mark points out, constructor injection might be the better way to go. That would look as follows:

public Parser(ValuesConfiguration configuration) {
    this(configuation, new DefaultValuesProvider());
}

public Parser(ValuesConfiguration configuration, ValuesProvider valuesProvider) {
    this.configuration = configuration;
    this.valuesProvider = valuesProvider;
}


public Result parse(String parameterName) {
    List<Values> values = valuesProvider.getValues(parameterName);
    // do other stuff on values
    // ...
    return valuesMapper.transformValues(values, configuration);
}

我也同意他的优势总结:

I also agree with his summary of advantages:

这将使您可以使依赖性为最终/只读.属性注入提供了关于变量的非常弱的保证-例如您可以继续更改同一实例上的依赖关系,而这可能并不是您想要的.

This would enable you to make the dependency final/readonly. Property Injection provides quite weak guarantees about invariants - e.g. you could keep changing the dependency on the same instance, and that's probably not what you want.

但是,它也有缺点:

  1. 构造函数的数量与可选参数的数量成指数关系,从而导致大量委托和参数的JavaDoc重复.因此,如果可选依赖项很少,我只会使用构造函数注入.您有2个,大约是我对这种事情的痛苦阈值.
  2. 构造函数的注入对子类的友好程度不是很高,因为子类的构造函数将不得不重新声明超类的依赖关系.在您的情况下,希望公开所有配置选项的子类将需要4个构造函数...如果添加可选参数,最好不要通过构造函数注入为此,因为这将需要另外4个构造函数.

因此,我的建议是使用setter注入,如果有必要防止重新分配依赖关系,请执行以下操作:

My recommendation therefore is to use setter injection, and if it is necessary to prevent the reassignment of dependences, do:

public void setValuesProvider(ValuesProvider valuesProvider) {
    if (this.valuesProvider != null) {
        throw new IllegalStateException("Dependency already set");
    }
    this.valuesProvider = valuesProvider;
}

这篇关于可配置的依赖关系,易于模拟默认实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆