创建对象工厂以提高可测试性并隐藏新运算符

时间:2016-09-23 15:55:56

标签: java unit-testing dependency-injection

我正在使用DI传递我的依赖项。但在某些情况下,我们需要动态创建对象,并且需要在初始化期间提供参数。 代码示例-a 尝试解释该方案。

为了初始化这种类型的对象并隐藏 new 运算符,我创建了简单的工厂。 代码示例-b

代码示例-a

int用于简化它们/实际上可以是某些真实对象

public class Sample {

    private final int c;
    public Sample(int c){
        this.c = c;
    }

    public void doSomething(SomeCommand command, Request request, Context context){
        DynamicDependency dynamicDependency = new DynamicDependency(command.getA(), command.getB(), c);
        dynamicDependency.doSomeWork(request, context);
    }

}

class DynamicDependency{

    private final int a;
    private final int b;
    private final int c;

    public DynamicDependency(int a, int b, int c){

        this.a = a;
        this.b = b;
        this.c = c;
    }

    public void doSomeWork(Request request, Context context){
        /*
        Do work
         */
    }
}

class SomeCommand {
    private int a;
    private int b;

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }


    public int getB() {
        return b;
    }

    public void setB(int b) {
        this.b = b;
    }
}

代码示例-b

public interface IParameterizedObjectFactory<T> {
    T getInstance(Object... arguments) throws ClassCastException;
}

public class DynamicDependency implements IParameterizedObjectFactory<DynamicDependency> {

@Override
public DynamicDependencyFactory getInstance(Object... arguments) throws ClassCastException {
        Validate.notNull(arguments);

        if(arguments.length > 0){
            final int a = (Integer) arguments[0];
            final int b = (Integer) arguments[1];
            final int c = (Integer) arguments[2];
            return new DynamicDependency(a, b,c);
        }
        return null;
    }
}

这可以完成工作,因为我现在可以注入工厂,然后使用它来获取新对象:

DynamicDependency dynamicDependency = dynamicDependencyFactory.getInstance(a,b,c);

问题(S):

虽然,它完成了工作,但我们需要传递Object [s]的列表,并且我们放松了强类型。施法也会占用一些执行时间。如何改进?

另一种方法可能是根本不使用接口,并使用具有适当参数列表的getInstance方法的具体类。对我来说听起来很合理。

public class DynamicDependencyFactory {

    public DynamicDependency getInstance(int a, int b, int c) {
        return new DynamicDependency(a, b,c);
    }
}

还可以采取哪些措施来隐藏?或者我应该使用第二种方法来创建具体工厂?

注意:我正试图远离反思

2 个答案:

答案 0 :(得分:0)

我决定使用混合方法,使用工厂,我无法控制正在创建的运行时对象,并通过控件与我一起的方法传递运行时数据。

史蒂文在评论中分享了几篇好文章,发布在这里。

Factories are a code smell runtime values should not be injected into a component's constructor

幸运的是,在运行时值的情况下,我已经避免了构造函数注入。问题在于遗留代码和我们团队不拥有的代码。对于现在,对于不属于我们的代码,我们必须使用构造函数,即使它会闻到一点:)

答案 1 :(得分:0)

你建议的第二种方法比第一种方法好得多。如果需要,您仍然可以从该工厂中提取接口:

public interface IDynamicDependencyFactory {
    DynamicDependency getInstance(int a, int b, int c);
}

请注意缺少泛型类型参数。您对以下界面的第一个建议:

public interface IParameterizedObjectFactory<T> {
    T getInstance(Object... arguments) throws ClassCastException;
}
根据您的示例,

似乎完全没有必要,并且正如您所指出的那样,Object[]作为参数使得它成为一个非常令人不愉快且非类型安全的API。

如果确实需要将不同的参数类型传递给工厂中的方法,那么为每个有效签名定义一个重载,而不是只接受Object[]

public interface IDynamicDependencyFactory {
    DynamicDependency getInstance(int a, int b, int c);

    DynamicDependency getInstance(double a, int b, BigDecimal c);
}

更好的是,如果您可以重构代码以便它不需要这样的工厂那么这可能是有益的(除非您无法同时访问RequestContext个对象时间为abc int值。例如,您可以将构造函数参数拉出为方法参数,并将DynamicDependency视为服务(或单例):

class DynamicDependencyService {
    public void doSomeWork(Request request, Context context, int a, int b, int c){
        //Do work
    }
}

这样,DynamicDependencyService的实例可以通过构造函数传递给Sample对象。