我对使用Autofac还是很陌生,并且对正在使用的构造函数注入方法感到困扰。这是场景:
我目前有两个类继承了IForms
接口。每个类也都有自己的接口
public interface IForms
{
long CreateNewForm(FormData data);
FormData RetrieveFormById(long id);
}
public interface IFormA : IForms
{ }
public interface IFormB : IForms
{ }
现在,我有一个可以像这样处理的类:
public class ApplicationForms : IApplicationForms
{
private readonly IFormA _formA;
private readonly IFormB _formB;
public ApplicationForms(IFormA formA, IFormB formB)
{
_formA = formA;
_formB = formB;
}
public void SubmitApplicationForm(FormData data)
{
switch(data.FormType)
{
case FormType.FormA:
_formA.CreateNewForm(data);
break;
case FormType.FormB:
_formB.CreateNewForm(data);
break;
}
}
}
现在有可能会有另外两种形式(例如FormC
,FormD
,FormE
)出现。这里将发生的是ApplicationForms
构造函数中将有3个以上的构造函数参数。
是否有一种方法可以将所有构造函数参数组合为一个参数?我可以看到它最终肯定看起来很难看。
答案 0 :(得分:2)
由于有一个通用的IForms
接口,因此您可以插入一个枚举。
这看起来像是策略模式的不错选择。
我建议重构基本接口,以便能够识别出它是哪种类型的表格
public interface IForms {
FormType FormType { get; }
long CreateNewForm(FormData data);
FormData RetrieveFormById(long id);
}
并更新依赖类
public class ApplicationForms : IApplicationForms {
private readonly IEnumerable<IForms> forms;
public ApplicationForms(IEnumerable<IForms> forms) {
this.forms = forms;
}
public void SubmitApplicationForm(FormData data) {
var form = forms.FirstOrDefault(_ => _.FormType == data.FormType);
if(form != null)
form.CreateNewForm(data);
//...
}
}
这假定在注册派生接口时,会将其与基本IForms
接口相关联。
var builder = new ContainerBuilder();
builder.RegisterType<FormA>().As<IForms>();
builder.RegisterType<FormB>().As<IForms>();
builder.RegisterType<FormC>().As<IForms>();
//...
现在,无论添加了多少表格,该类都可以执行而无需修改。
答案 1 :(得分:2)
您要描述的问题是您有很多表单,但是在运行时需要一种特定的表单,因此您不想注入所有表单。对于抽象工厂来说,这可能是个好方案。
我们通常将工厂表示为具有单个方法的接口,但是我们也可以使用委托来实现:
public delegate IForm GetFormByTypeFunction(FormType formType);
现在您的课程如下:
public class ApplicationForms : IApplicationForms
{
private readonly GetFormByTypeFunction _getFormByType;
public ApplicationForms(GetFormByTypeFunction getFormByType)
{
_getFormByType = getFormByType;
}
public void SubmitApplicationForm(FormData data)
{
var form = _getFormByType(data.FormType);
form.CreateNewForm(data);
}
}
现在的问题是如何实施工厂。它可能仍然有一个switch
语句,或者看起来不太优雅的东西,但这没关系。工厂的要点是无论它如何工作,创建和/或选择实现的业务都将脱离依赖于实现的类。
您可以像这样在Autofac中注册代表:
builder.Register<GetFormByTypeFunction>(context => formType =>
{
switch (formType)
{
case FormType.Type1:
{
return context.Resolve<FormOne>();
}
case FormType.Type2:
{
return context.Resolve<FormTwo>();
}
default:
throw new InvalidOperationException("Unknown form type");
}
});
现在,您不需要预先解决所有IForm
的实现,因为一旦知道要使用的实现,就可以直接从容器中解决需要的实现。
这似乎是“错误的”,因为您正在从容器中进行解析。但是您不是直接从容器中解析 。您要依靠一家工厂。该工厂可以用任何其他实现替换,这意味着您的类不依赖于容器。
这种工厂也非常容易模拟。从技术上讲,它甚至不是一个模拟。只是工厂的一个实现,它返回一个模拟。
var formMock = new Mock<IForm>();
var factory = new GetFormByTypeFunction(formType => formMock.Object);