У меня должен быть отдельный блок для интерфейсов?

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

Теперь, мое личное отношение - то, что эти интерфейсы должны быть помещены в отдельное пространство имен в рамках того же блока (таким образом, у нас есть a MyCompany.CoolApp.DataAccess блок, и в котором существует Interfaces предоставление пространства имен MyCompany.CoolApp.DataAccess.Interfaces).

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

Есть ли инструкции по лучшей практике для этого?

Править:

Высказывать мое немного более ясное мнение: Мы уже разделяем UI, DataAccess, DataModel и другие вещи в различные блоки. Мы можем также в настоящее время выгружать нашу реализацию с другой реализацией без любой боли, поскольку мы отображаем класс с реализацией на интерфейс с помощью Единицы (платформа МОК). Я должен указать, что мы никогда не пишем две реализации того же интерфейса, за исключением причин полиморфизма и создающих насмешек для поблочного тестирования. Таким образом, мы в настоящее время "не выгружаем" реализацию кроме модульных тестов.

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

Я могу, однако, видеть точку, что наличие их в другом блоке означает, что разработчики не будут случайно "новый" класс с реализацией, а не иметь созданное использование обертки МОК.

Одна точка, которую я не понял из ответов, является проблемой развертывания. Если я буду только в зависимости от интерфейсных блоков, то у меня будет что-то как следующая структура:

MyCompany.MyApplication.WebUI
    References:
        MyCompany.MyApplication.Controllers.Interfaces
        MyCompany.MyApplication.Bindings.Interfaces
        etc...

Когда я создаю это, блоки, которые автоматически помещаются в папку мусорного ведра, являются просто теми интерфейсными блоками. Однако мои отображения типа в единице отображают различные интерфейсы на свою фактическую реализацию. Как делают блоки, которые содержат мои реализации, заканчиваются в папке мусорного ведра?

51
задан Gilles 'SO- stop being evil' 4 April 2013 в 02:15
поделиться

3 ответа

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

Однако, размышляя об этом дальше, я не могу вспомнить множество реальных примеров такой практики (например, do log4net или NUnit предоставляют сборки общедоступного интерфейса, чтобы потребители могли затем определитесь с различными реализациями? Если да, то какую другую реализацию nunit я могу использовать?). Просматривая целую вечность в Google, я нашел ряд ресурсов.

14
ответ дан 7 November 2019 в 10:21
поделиться

Обычная обычная ожидаемая ? практика состоит в том, чтобы поместить их в их собственную сборку, потому что тогда данный проект, использующий эти интерфейсы, не требует жесткого ссылка на реализацию этих интерфейсов. Теоретически это означает, что вы можете поменять реализацию практически без боли.

Тем не менее, я не могу вспомнить, когда делал это в последний раз, но, по мнению @David_001, это не обязательно «обычно». Мы стремимся согласовывать наши интерфейсы с реализацией, и чаще всего мы используем интерфейсы для тестирования.

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

В сценарии развертывания, если вы изменили реализацию, вы могли бы теоретически просто развернуть эту единственную DLL - таким образом, оставив, скажем, UI и интерфейсные DLL в покое. Если вы скомпилировали свои интерфейсы и реализацию вместе, вам может потребоваться повторно развернуть UI DLL ...

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

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

27
ответ дан 7 November 2019 в 10:21
поделиться

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

  1. Разделяемые интерфейсы.
  2. DTO.
  3. Исключения.

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

Затем у меня есть дизайн типа времени выполнения, в котором я устанавливаю свой контейнер DI при запуске приложения или при запуске набора модульных тестов. Таким образом, существует четкое разделение между реализациями и тем, как я могу варьировать их с помощью DI. Мои клиентские модули никогда не имеют прямых ссылок на реальные библиотеки ядра, только на библиотеку "SharedTypes".

Ключевым для моего дизайна является наличие общей концепции времени выполнения для клиентов (будь то приложение WPF или NUnit), которая устанавливает необходимые зависимости, т.е. конкретные реализации или своего рода mocks\stubs.

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

Обновление

Для пояснения на примере того, как зависимости оказываются в целевом приложении.

В моей ситуации у меня есть клиентское приложение WPF. Я использую Prism и Unity (для DI), где, что важно, Prism используется для композиции приложения.

В Prism сборка вашего приложения - это просто оболочка, фактическая реализация функциональности находится в сборках "Модулей" (вы можете иметь отдельную сборку для каждого концептуального Модуля, но это не требование, у меня одна сборка Модулей ATM). В обязанности оболочки входит загрузка Модулей - композиция этих Модулей и есть приложение. Модули используют сборку SharedTypes, но оболочка ссылается на конкретные сборки. Конструкция типов времени выполнения, которую я обсуждал, отвечает за инициализацию зависимостей, и это делается в оболочке.

Таким образом, сборки модулей, обладающие всей функциональностью, не зависят от конкретных реализаций. Они загружаются оболочкой, которая сортирует зависимости. Оболочка ссылается на конкретные сборки, и таким образом они попадают в каталог bin.

Эскиз зависимости:

Shell.dll <-- Application
  --ModuleA.dll
  --ModuleB.dll
  --SharedTypes.dll
  --Core.dll
  --Common.dll + Unity.dll <-- RuntimeDI

ModuleA.dll
  --SharedTypes.dll
  --Common.dll + Unity.dll <-- RuntimeDI

ModuleB.dll
  --SharedTypes.dll
  --Common.dll + Unity.dll <-- RuntimeDI

SharedTypes.dll
  --...
9
ответ дан 7 November 2019 в 10:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: