Подтвердить что ты не робот

Являются ли примитивные параметры конструктора плохими идеями при использовании контейнера IoC?

Стандартный отказ от новичка: я новичок в IoC и получаю смешанные сигналы. Я ищу некоторые рекомендации по следующей ситуации.

Предположим, что у меня есть следующий интерфейс и реализация:

public interface IImageFileGenerator
{
    void RenameFiles();
    void CopyFiles();
}

public class ImageFileGenerator : IImageFileGenerator
{
    private readonly IList<IImageLink> _links;
    private readonly string _sourceFolder;
    private readonly string _destinationFolder;
    private readonly int _folderPrefixLength;

    public ImageFileGenerator(IList<IImageLink> links, string sourceFolder, string destinationFolder)
    {
        _links = links;
        _sourceFolder = sourceFolder;
        _destinationFolder = destinationFolder;
        _folderPrefixLength = 4;
    }

    public void RenameFiles()
    {
        // Do stuff, uses all the class fields except destination folder
    }

    public void CopyFiles()
    {
        // Do stuff, also uses the class fields
    }
}

Я смущаюсь, должен ли я только отправлять интерфейс/зависимости конструктору, создавать некоторый объект параметра и передавать его конструктору или сохранять его как есть и передавать параметры во время разрешения экземпляра.

Итак, есть ли более правильный способ настроить этот код для лучшей работы с контейнером IoC? Можно ли было бы выбрать один из следующих вариантов по моему текущему расположению?

1.

public interface IImageFileGenerator
{
    void RenameFiles(IList<IImageLink> links, string sourceFolder);
    void CopyFiles(IList<IImageLink> links, string sourceFolder, stringDestFolder);
}

public class ImageFileGenerator : IImageFileGenerator
{
    private readonly int _folderPrefixLength;

    public ImageFileGenerator()
    {
        _folderPrefixLength = 4;
    }

    public void RenameFiles(IList<IImageLink> links, string sourceFolder)
    {
        // Do stuff
    }

    public void CopyFiles(IList<IImageLink> links, string sourceFolder, stringDestFolder)
    {
        // Do stuff
    }
}

Мне не нравится, что я передаю то же самое в обоих случаях (кроме папки назначения). В текущей реализации IImageFileGenerator мне нужно выполнить оба метода, и для каждого метода необходимы одинаковые значения. Вот почему я передал состояние через конструктор.

2.

public interface IImageFileGenerator
{
    void RenameFiles();
    void CopyFiles();
}

public class ImageLinkContext
{
    // various properties to hold the values needed in the
    // ImageFileGenerator implementation.
}

public class ImageFileGenerator : IImageFileGenerator
{
    private readonly IList<IImageLink> _links;
    private readonly string _sourceFolder;
    private readonly string _destinationFolder;
    private readonly int _folderPrefixLength;

    public ImageFileGenerator(ImageLinkContext imageLinkContext)
    {
        // could also use these values directly in the methods 
        // by adding a single ImageLinkContext field and skip 
        // creating the other fields
        _links = imageLinkContext.ImageLinks;
        _sourceFolder = imageLinkContext.Source;
        _destinationFolder = imageLinkContext.Destination;
        _folderPrefixLength = 4;
    }

    public void RenameFiles()
    {
        // Do stuff, uses all the class fields except destination folder
    }

    public void CopyFiles()
    {
        // Do stuff, uses all the class fields
    }
}

Этот подход может быть даже изменен в Службе фасадов (ранее называемой агрегированными услугами), как упоминается Марк Сееманн здесь.

Я также читал, что вы можете использовать свойства для этих значений и использовать инъекцию свойств, хотя кажется, что это больше не предпочтительнее (autofac упоминает, что встраивание конструктора предпочтительнее... Ninject Я считаю, что даже удалил возможность в версии 2).

В качестве альтернативы я прочитал, что вы также можете создать метод инициализации и убедиться, что свойства там установлены.

Так много вариантов, и я становлюсь более смущенным, когда читаю больше об этом. Я уверен, что нет окончательного правильного пути (или, может быть, есть, по крайней мере, для этого примера???), но, возможно, кто-то может обеспечить плюсы и минусы каждого подхода. Или, может быть, есть другой подход, который я полностью пропустил.

Теперь я понимаю, что этот вопрос, вероятно, немного касается субъективной стороны (и это действительно более одного вопроса), но я надеюсь, что вы можете простить меня и дать некоторые рекомендации.

PS - В настоящее время я пытаюсь использовать автофак в том случае, если это повлияет на дизайн, который лучше подходит.

ПРИМЕЧАНИЕ. Я немного изменил код о целевой папке... он не используется RenameFiles (может иметь отношение к вашему ответу).

4b9b3361

Ответ 1

Ну, я закончил тем, что перепроектировал это после прочтения книги Injection Dependency in.Net (я настоятельно рекомендую эту книгу любому объектно-ориентированному разработчику, а не только разработчикам .Net а не только тех, кто заинтересован в использовании контейнера IoC!).

В сборке "Домен" теперь есть следующее:

public interface IImageFileService
{
    void RenameFiles();
    void CopyFiles(); 
}

public interface IImageLinkMapRepository
{
    IList<ImageLink> GetImageLinks(); 
}

Затем в сборке FileAccess я создал реализации для этих интерфейсов:

public class ImageFileService : IImageFileService
{
    public ImageFileService(IImageLinkMapRepository repository)
    {
        // null checks etc. left out for brevity
        _repository = repository;
    }

    public void RenameFiles()
    {
        // rename files, using _repository.GetImageLinks(), which encapsulates
        // enough information for it to do the rename operations without this
        // class needing to know the specific details of the source/dest dirs.
    }

    public void CopyFiles() 
    { 
        // same deal as above
    }
}

По сути, я удалил необходимость примитивных типов в моем конструкторе, по крайней мере для этого класса. В какой-то момент мне понадобилась эта информация, но она была введена в ImageLinkMapRepository, где информация имела больше смысла. Я использовал идентификационные параметры autofac для обработки их инъекций.

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

Ответ 2

В вашем примере то, что вы на самом деле проходите, это зависимости, но, кроме того, данные, необходимые для работы класса.

В вашем случае это звучит так, как методы RenameFiles() и CopyFiles() работают с параметрами, которые им передаются. Учитывая их имена, я бы подумал, что методы в одном экземпляре ImageFileGenerator можно вызывать с разными параметрами. Если это правда, возможно, параметры должны быть в методе самим вызовом, а не конструктором.

Если, с другой стороны, в одном экземпляре RenameFiles() и CopyFiles() каждый из них вызывается только один раз с теми же параметрами, параметры будут хорошими кандидатами для конструктора.

Я лично попытался бы избежать инъекции свойств для требуемых зависимостей - в этом случае инъекция конструктора гораздо более уместна.