Выбор между ограничением и свободой (интерфейсы и внедрение зависимостей) - PullRequest
0 голосов
/ 07 июня 2018

Насколько я понимаю, интерфейсы в C # могут рассматриваться как контракт или обещание, которому должен следовать производный класс.Это позволяет различным объектам вести себя по-разному при вызове переопределенного метода.

DI, насколько я понимаю, предлагает возможность уменьшить зависимости, позволяя вводить зависимость (обычно через контейнер) через ctor, свойство или метод.

Похоже,это две совершенно противоположные силы между свободой и сдержанностью.Я могу создать контракт, в котором говорится, что производный класс ДОЛЖЕН следовать этим определенным принципам, который по своей природе является ограничительным по сравнению с DI, который позволяет мне вводить зависимости (я знаю, что зависимость должна наследовать интерфейс, но все же ...), который по своей природе разрешительный,Я могу делать все инъекционно и дико и менять класс.

Полагаю, мой вопрос в том, как вы решаете, насколько ограничительным вы хотите быть?Когда более важно использовать интерфейсы для полиморфизма или DI, когда вы хотите полную свободу?Где вы проводите черту?Можно ли использовать интерфейсы, когда вы хотите, чтобы структура (методы и свойства) выравнивались между различными производными классами и DI, когда параметры могут сильно отличаться?

EDIT:

Может быть, DI былнеправильный пример.Допустим, у меня есть фабрика IPlugin и плагинов.Все плагины нуждаются в одинаковой информации, работают одинаково и т. Д. Поэтому имеет смысл использовать интерфейс.Теперь один плагин работает одинаково, но ему нужны другие параметры или разные данные, но в конечном итоге одна и та же структура, например, Load, Run и т. Д.

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

Ответы [ 2 ]

0 голосов
/ 13 июня 2018

Я читал «Искусство модульного тестирования» Роя Ошерова, и я думаю, что он отвечает на мой вопрос.

Он говорит о DI в тестировании и упоминает, что некоторые люди считают, что это вредит принципам объектно-ориентированного проектирования (а именнонарушение инкапсуляции).

Существуют объектно-ориентированные принципы для обеспечения ограничений для конечного пользователя API, чтобы объектная модель использовалась правильно и была защищена от непреднамеренного использования.Тесты, например, добавить другого конечного пользователя.Инкапсуляция этих внешних зависимостей где-то, не позволяя никому их изменять, частные конструкторы или запечатанные классы, наличие не виртуальных методов - все это классические признаки чрезмерной защиты дизайна.

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

0 голосов
/ 07 июня 2018

DI, насколько я понимаю, предлагает возможность уменьшить зависимости, позволяя вводить зависимость (обычно через контейнер) через ctor, свойство или метод.

Неправильно.Статический анализ кода показывает, что зависимость все еще существует.Я просто изменяю то, как вы получили экземпляр объекта.Если ваша ctor говорит, что ожидает объекты определенного класса, то у вас есть зависимость от этого типа, но у вас также есть сильная связь с классом, который плохой .Однако, если ваш ctor ожидает типы определенного interface, то у вас есть зависимость от этого определения контракта, но не от фактической реализации ( потеря связи ).

Насколько я понимаю, интерфейсы в C # могут рассматриваться как контракт или обещание, которому должен следовать производный класс.Это позволяет различным объектам вести себя по-разному, когда вызывается переопределенный метод.

Да, но они также являются способом абстрагировать компонент, скрывая ненужные детали, что-то, что класс не может.Вот почему в достаточно больших системах хорошо иметь MyContracts.dll, в котором вы определяете все свои interface s;и скажем BusinessLogic.dll и ClientStuff.dll.ClientStuff зависит от контрактов, но не заботится о фактической реализации и возможных миллионах других зависимостей, которые может иметь BusinessLogic.dll (скажем, типичное приложение WPF или WCF).

Когда более важно использоватьинтерфейсы для полиморфизма или DI, когда вы хотите полную свободу?Где вы проводите черту?Можно ли использовать интерфейсы, когда вы хотите, чтобы структура (методы и свойства) выравнивались между различными производными классами и DI, когда параметры могут сильно отличаться?

DI не предлагает больше свободы, чем aсистема без DI.Однако я думаю, что вы можете быть немного смущены терминологией или понятиями.Я всегда хорош.Внедрение интерфейсов через типы классов лучше, как я уже упоминал выше.

Я могу создать контракт, в котором говорится, что производный класс ДОЛЖЕН следовать этим определенным правилам, который по своей природе является ограничительным по сравнению с DI, что позволяет вводить зависимости

Контракт - это контракт, который является контрактом в форме интерфейса или абстрактного класса.Если конструктор (внедрение-конструктор) или свойство (внедрение-свойство) запрашивает MyDbConnection, то это устанавливает требование относительно того, что может быть введено , тем более , чем просто ожидание IMyDbConnection.

Я думаю, что вы можете ошибаться в отношении того, что делает DI.DI не всегда внедряет классы и интерфейсы не только для полиморфизма.Последнее является распространенной ошибкой.DI не имеет никакого отношения к тому, чтобы быть "ограничительным" .От вас зависит, какой тип вы ожидаете внедрить.

Фактически ожидание, что конкретный объект класса или что-то, производное от абстрактного класса, будет внедрено, будет более «ограничительным» , чемВнедрение интерфейса по самой природе того, как интерфейсы могут быть повторно использованы в других сценариях.Например, не то, чтобы вы вводили один, но INotifyPropertyChanged с предварительным кодом WPF, но не играли огромную роль в WPF и MVVM.

Ограничительный:

public EditPatientViewModel (SqlPersistenceService svc) {}

Не такой ограничительный

public EditPatientViewModel (IPersistenceService svc) {}

Выбор между ограничением и свободой (Интерфейсы и внедрение зависимостей)

Это зависит только от вас, но если вы добавите интерфейсы, то получите:

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