减少增加的构造函数服务参数

时间:2019-06-03 09:30:14

标签: c# dependency-injection autofac autofac-configuration

我对使用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;
        }
    }
}

现在有可能会有另外两种形式(例如FormCFormDFormE)出现。这里将发生的是ApplicationForms构造函数中将有3个以上的构造函数参数。

是否有一种方法可以将所有构造函数参数组合为一个参数?我可以看到它最终肯定看起来很难看。

2 个答案:

答案 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);