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

Должен ли я иметь отдельную сборку для интерфейсов?

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

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

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

Есть ли рекомендации для этой практики?

EDIT:

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

  • Имеют ли отдельные сборки подразумеваемые свободные соединения? Ниже приведено следующее:

    http://www.theserverside.net/tt/articles/showarticle.tss?id=ControllingDependencies

    http://codebetter.com/blogs/jeremy.miller/archive/2008/09/30/separate-assemblies-loose-coupling.aspx

  • Общий консенсус, который я смог найти в googling, заключался в том, что меньшее количество сборок лучше, если нет веских оснований для добавления новых сборок. См. Также:

    http://www.cauldwell.net/patrick/blog/ThisIBelieveTheDeveloperEdition.aspx

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

Ответ 2

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

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

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

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

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

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

Ответ 3

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

  • Общие интерфейсы.
  • DTOS.
  • Исключения.

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

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

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

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

Обновление

Чтобы пояснить пример использования зависимостей в целевом приложении.

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

С помощью Prism ваша сборка приложений - это всего лишь оболочка, фактические реализации функциональности находятся в сборках "Модуль" (у вас может быть отдельная сборка для каждого концептуального модуля, но это не является обязательным требованием, у меня есть один банкомат сборки модулей). Ответственность за загрузку модулей лежит на оболочке - состав этих модулей является приложением. Модули используют сборку 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
  --...

Ответ 4

Я согласен с тикающим ответом. Хорошо для тебя, Дэвид. На самом деле, я с облегчением увидел ответ, подумал, что сошел с ума.

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

Другим сумасшедшим является одно пространство имен на каждую бессмысленность сборки. Таким образом, вы получаете пространство имен SomeBank.SomeApp.Interfaces и все в нем.

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

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

У меня никогда не возникало проблемы с логикой DI или свопинга позже.

• Сборки .NET - это единица безопасности, область применения и развертывания API и не зависят от пространств имен.

• Если две сборки зависят друг от друга, они не могут быть развернуты и версированы отдельно и должны быть объединены.

• Наличие большого количества DLL часто означает создание множества публичных материалов, так что трудно сказать фактический публичный API от членов типа, которые должны были быть опубликованы, потому что они были произвольно помещены в собственную сборку.

• Должен ли код вне моей DLL использовать мой тип?

• Начать консервативный; Обычно я могу легко перемещать тип слоя, его немного сложнее в обратном направлении.

• Могу ли я аккуратно упаковать свою область или фреймворк в пакет NuGet, чтобы он был полностью опциональным и доступным для версии, как и любой другой пакет?

• Являются ли мои типы согласованными с доставкой функции и могут ли они помещаться в пространство имен функций?

• Многие реальные библиотеки и фреймворки заклеймены, что упрощает их обсуждение, и они не сжигают имена пространства имен, которые предполагают его использование или являются двусмысленными, могу ли я подкрепить компоненты своего приложения, используя "кодовые имена", такие как Steelcore, а не generic clichéd и запутывающие термины, errm 'Services'?

Edit

Это одна из непонятых вещей, которые я вижу в развитии сегодня. Это так плохо.

У вас есть API, поэтому поместите все его типы в один проект API. Выдвигайте их только тогда, когда вам нужно их использовать/повторно использовать. Когда вы их перемещаете, переместите их прямо в пакет NuGet с четким именем, которое несет намерение и фокус пакета. Если вы боретесь за имя и считаете "Common", возможно, потому, что вы создаете свалку.

Вы должны включить свой пакет NuGet в семейство связанных пакетов. Ваш "основной" пакет должен иметь минимальные зависимости от других пакетов. Типы внутри связаны с использованием и зависят друг от друга.

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

Факторинг библиотеки

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

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

Что это.

Лука

Ответ 5

Я смотрю на System.Data.dll(4.0) в обозревателе объектов, и я вижу, что он автономен сам по себе не только с интерфейсами, но и с такими инструментальными классами, как DataSet, DataTable, DataRow, DataColumn и т.д. Более того, просматривая список пространств имен, таких как System.Data, System.Data.Common, System.Configuration и System.Xml, сначала предлагает интерфейсы, содержащиеся в их собственных сборках, со всеми соответствующими и необходимыми кодами, хранящимися вместе и вторыми и, что более важно, повторно использовать те же пространства имен в общем приложении (или каркасе), чтобы также сортировать классы.