2

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

现在有可能还会有 2 种形式出现(例如FormC, FormD, FormE)。这里会发生的是构造函数中多了 3 个构造函数参数ApplicationForms

有没有一种方法可以将所有构造函数参数组合成一个参数?我可以看到它最终肯定会看起来很丑。

4

2 回答 2

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>();

//...

现在,无论添加多少表单,该类都可以在不修改的情况下执行。

于 2019-06-03T11:41:40.787 回答
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);
于 2019-06-03T14:04:59.883 回答