Уменьшить увеличивающиеся параметры сервиса Конструктора - PullRequest
2 голосов
/ 03 июня 2019

Я все еще новичок в использовании 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).Что здесь произойдет, так это то, что в конструкторе ApplicationForms будет еще 3 параметра конструктора.

Есть ли способ объединить все параметры конструктора в один параметр?Я вижу, что в конце концов это будет выглядеть ужасно.

Ответы [ 2 ]

2 голосов
/ 03 июня 2019

Проблема, которую вы описываете, состоит в том, что у вас много форм, но во время выполнения вам нужна одна конкретная форма, и поэтому вы не хотите внедрять все формы.Это может быть хорошим сценарием для абстрактной фабрики.

Мы часто представляем фабрику как интерфейс с одним методом, но мы также можем сделать это с делегатом:

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);
2 голосов
/ 03 июня 2019

Поскольку существует общий интерфейс 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 interface.

var builder = new ContainerBuilder();
builder.RegisterType<FormA>().As<IForms>();
builder.RegisterType<FormB>().As<IForms>();
builder.RegisterType<FormC>().As<IForms>();

//...

Теперь независимо от того, сколько еще форм добавлено, класс может выполнять без изменений.

...