Является ли смешивание инъекций на основе конструктора и сеттера плохим?

У меня есть класс для импорта продуктов из файла CSV, для которого требуется около 7 параметров. Это информация, которая определенно необходима импортеру.

Все эти параметры имеют одинаковый срок службы. В итоге у нас должен быть неизменяемый объект.

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

Вопросы:

1) Является ли смешивание инъекций на основе конструктора и сеттера плохой практикой?

2) Как можно решить эту конкретную проблему?

Я думал о применении рефакторинга Мартина Фаулера «Ввести объект параметра», но с этим возникла проблема.

4 Параметры могут быть легко перемещены в объект Parameter (customerId, projectId, languageId и т.д.) - все целые числа.

Другие 3 параметра - это объект, который я ввожу (требуется для модульных тестов Mock).


person Nikita Fedyashev    schedule 31.12.2009    source источник
comment
Это будет зависеть от вашего контейнера DI... некоторые делают это проще, чем другие.   -  person skaffman    schedule 31.12.2009
comment
@skaffmann: я категорически не согласен. Использование шаблонов внедрения зависимостей не должно диктоваться выбором контейнера внедрения зависимостей — контейнер должен помогать вам, а не ограничивать вас.   -  person Mark Seemann    schedule 31.12.2009
comment
@Nikita - почему бы не ввести полный объект параметра и не внедрить свои макеты в параметр?   -  person Jeff Sternal    schedule 31.12.2009


Ответы (1)


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

Важно понимать, когда использовать каждый шаблон.

  • Внедрение конструктора должно быть шаблоном внедрения по умолчанию. Его очень легко реализовать, и он может гарантировать инварианты: назначьте его в поле только для чтения, чтобы обеспечить инварианты потребителя.
  • Внедрение свойств можно использовать, если у вас есть хорошая реализация Local Default, но вы хотите следовать Принцип Открытости/Закрытости и позволить опытным пользователям расширить класс, предоставив альтернативную реализацию.

Вы никогда не должны применять внедрение свойств из-за косметики конструктора.

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

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

Представьте, что ваш исходный конструктор выглядит так:

public MyClass(IDep1 dep1, IDep2 dep2, IDep3 dep3, IDep4 dep4, IDep5 dep5)

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

public class AggService : IAggService
{
    public AggService(IDep1 dep1, IDep3 dep3, IDep4 dep4)
    {
        // ...
    }

    // ...
}

Теперь вы можете переписать исходный конструктор следующим образом:

public MyClass(IAggService aggSrvc, IDep2 dep2, IDep5 dep5)

и так далее...

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

person Mark Seemann    schedule 31.12.2009
comment
Чем сервис агрегирования отличается от объекта параметра? Похоже, вы предполагаете, что это может иметь более выразительное, конкретное отношение к модели предметной области, но это должно быть верно для хорошо спроектированных объектов параметров, не так ли? - person Jeff Sternal; 31.12.2009
comment
@Jeff Sternal: служба агрегации может не сильно отличаться от объекта параметра, но службы агрегации больше склоняются к голливудскому принципу (хорошая вещь). Объекты параметров обычно представляют собой просто структуры, содержащие связанные объекты, которые можно извлекать и использовать по отдельности. Агрегирующая служба скроет эти зависимости от потребителя и вместо этого предложит общую функциональность. Хотя это далеко не четкое различие... - person Mark Seemann; 31.12.2009
comment
@Mark: Могу ли я найти больше информации о различии между Parameter Object и Aggregating service в вашей книге Dependency Injection in .NET? - person Nikita Fedyashev; 31.12.2009