C# 具有大量DI参数的多态抽象类
我经常遇到这样的问题:有一个抽象类来完成所有繁重的工作,然后我有很多多态类来定制抽象以满足特定的需要。抽象通常需要很多参数,因此它们都必须从所有多态类传递C# 具有大量DI参数的多态抽象类,c#,unit-testing,dependency-injection,autofac,C#,Unit Testing,Dependency Injection,Autofac,我经常遇到这样的问题:有一个抽象类来完成所有繁重的工作,然后我有很多多态类来定制抽象以满足特定的需要。抽象通常需要很多参数,因此它们都必须从所有多态类传递 public class FooComplex : AbstractFoo { public FooComplex(IBarAwesome awesome, IBarCool cool, ...) : base(IBarAwesome awesome, IBarCool cool, ...) { } ...a lot of o
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
}
我能做些什么来不通过所有这些东西,但能够对它们进行单元测试吗?我一直被教导这样做
var awesome = container.Resolve<IBarAwesome>();
var awesome=container.Resolve();
比如说,构造函数是一种糟糕的做法
我之所以想找到解决办法,因为我必须复制相同的参数并将其传递到许多多态子类中,所以将任何新的参数传递到抽象类中会变得越来越困难。我相信这与注释中提到的@C.Evenhuis类似,它将构造函数参数抽象到一个公共接口中,以便它们可以作为单个构造函数参数,易于测试 具体类别:
public class FooComplex : AbstractFoo
{
public FooComplex(ComplexParam complexParam) : base(complexParam)
{}
}
public class FooSimple : AbstractFoo
{
public FooSimple(SimpleParam simpleParam) : base(simpleParam)
{}
}
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; }
}
单一通用混凝土等级(可选)
使用该类,您可以将任何类型传递到继承iparms
的构造函数中,并可能消除对foomplex
和FooSimple
的需要
public class Foo<T> : AbstractFoo where T : IParam
{
public Foo(T param) : base(param)
{ }
}
接口:
public class FooComplex : AbstractFoo
{
public FooComplex(ComplexParam complexParam) : base(complexParam)
{}
}
public class FooSimple : AbstractFoo
{
public FooSimple(SimpleParam simpleParam) : base(simpleParam)
{}
}
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; }
}
需要做的就是:
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>();
builder.RegisterAggregateService();
感谢@Travis Illig和@C.Evenhuis帮助我想出这个解决方案
对于同一问题的更复杂的解决方案,请查看@Kitson88如果您希望这些参数随时间变化,一种解决方案是将它们放在一个
AbstractFooParameters
类中,具体类作为单个参数传递。不确定这是否是你想要的。这能用于DI吗?我不知道autofac,但它应该与构造函数注入一起工作,就像任何其他构造函数参数一样。我真的喜欢这个解决方案。你能把它作为答案发布吗?@IanOverton,我同意C.Evenhuis的解决方案是我关于如何解决这个问题的最初想法,但是实现在很大程度上取决于这些参数的上下文,以及您试图从“使用Autofac解决此问题”的角度实现什么,允许您定义一个接口,其中所需参数是接口上的属性。Autofac将动态生成接口的具体实现,并为您填充属性@TravisIllig没有使用Autofac的乐趣。现在只是浏览一下文档…非常令人印象深刻。参数列表在两个类中是相同的,而不是动态列表。只是课程的细节不同。特拉维斯说的很有趣,我得试试。它将使我不需要一个具体的类来完成我想做的事情。尽管我不需要这种解决这个问题的方法,但这绝对是一个很棒的方法,我必须找到使用它的地方。