Polymorphic Abstract class with a lot of DI paramters

时间:2018-04-18 18:02:15

标签: c# unit-testing dependency-injection autofac

I'm constantly running into the problem of having an abstract class that does all the heavy lifting and then I have a lot of polymorphic classes that customize the abstract to a specific need. The abstract generally needs a lot of parameters, so they all have to be passed from all polymorphic classes

public class FooComplex : AbstractFoo {
    public FooComplex(IBarAwesome awesome, IBarCool cool, ...) : base(IBarAwesome awesome, IBarCool cool, ...) { }
    ...a lot of overriding abstracts
}

public class FooSimple : AbstractFoo
{
    public FooSimple(IBarAwesome awesome, IBarCool cool, ...) : base(IBarAwesome awesome, IBarCool cool, ...) { }
    ...little bit of overriding abstracts
}

public class AbstractFoo
{
    public AbstractFoo(IBarAwesome awesome, IBarCool cool, ...)
    ...heavy lifting
}

Is there anything I can do to not pass all these things, but be able to unit test them? I've always been taught that doing

var awesome = container.Resolve<IBarAwesome>();

In like say the constructor is bad practice.

The reason I would like to find a solution to this, is it makes it harder and hard to pass anything new into the abstract class as I have to copy and pass the same parameters into many polymorphic subclasses.

2 个答案:

答案 0 :(得分:1)

我认为这类似于评论中提到的 @ C.Evenhuis ,它将构造函数参数抽象为公共接口,因此它们可以作为单个构造函数参数传递,也可以轻松测试。

具体课程:

public class FooComplex : AbstractFoo
{
    public FooComplex(ComplexParam complexParam) : base(complexParam)
    {}
}

public class FooSimple : AbstractFoo
{
    public FooSimple(SimpleParam simpleParam) : base(simpleParam)
    {}
}

单一通用混凝土类(可选)

使用此类,您可以将任何类型传递到继承IParams的构造函数中,并且可能无需FooComplexFooSimple

public class Foo<T> : AbstractFoo where T : IParam
{
    public Foo(T param) : base(param)
    { }
}

基础抽象类:

public abstract class AbstractFoo
{
 protected AbstractFoo(IParam parameter) { }
}

<强>接口

public interface IBarCool : IBar
{}

public interface IBarAwesome : IBar
{}

public interface IBar
{}

public interface IParam
{
    IEnumerable<IBar> Param { get; }
}

可重复使用的混凝土参数:

我个人不喜欢下面这种方法因为重复,但我想如果每个类都有自己独立的实现,那么它没关系。另一个选择是只有一个名为ParameterHolder的类,并且该类的两个实例被恰当地命名,例如var complex = new ParameterHolder()并传递给通用Foo<T>

public class ComplexParam : IParam
{
    public IEnumerable<IBar> Param { get; }

    public ComplexParam(IEnumerable<IBar> complexParam)
    {
        Param = complexParam;
    }
}

public class SimpleParam : IParam
{
    public IEnumerable<IBar> Param { get; }

    public SimpleParam(IEnumerable<IBar> simpleParam)
    {
        Param = simpleParam;
    }
}

答案 1 :(得分:0)

所有需要发生的事情是:

public interface IAbstractParams
{
   IBarAwesome awesome { get; } 
   IBarCool cool { get; }
   ... 
}

public class FooComplex : AbstractFoo 
{
    public FooComplex(IAbstractParams params) : base(params) { }
    ...a lot of overriding abstracts
}

public class FooSimple : AbstractFoo
{
    public FooSimple(IAbstractParams params) : base(params) { }
    ...little bit of overriding abstracts
}

public class AbstractFoo
{
    protected readonly IBarAwesome _awesome;
    protected readonly IBarCool _cool;

    public AbstractFoo(IAbstractParams params)
    {
     _awesome = params.awesome;
     _cool = params.cool;
    }

    ...heavy lifting
}

然后你需要添加nuget包Autofac.Extras.AggregateService并将此行添加到你的构建器:

builder.RegisterAggregateService<IAbstractParams>();

感谢 @Travis Illig @ C.Evenhuis 帮助我提出此解决方案。

对于同样问题的更复杂的解决方案,请查看 @ Kitson88