Должен ли я иметь отдельную сборку для интерфейсов? - PullRequest
50 голосов
/ 29 июля 2010

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

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

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

Существуют ли рекомендации для этого?

РЕДАКТИРОВАТЬ:

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

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

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

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

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

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

Ответы [ 6 ]

26 голосов
/ 29 июля 2010

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

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

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

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

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

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

14 голосов
/ 01 августа 2010

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

Однако, подумав об этом далее, яне могу придумать много примеров из этой практики в реальном мире (например, do 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

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

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

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

9 голосов
/ 29 июля 2010

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

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

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

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

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

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

Обновление

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

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

В 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
  --...
6 голосов
/ 05 июня 2015

Я согласен с отмеченным ответом.Молодец, Дэвид.На самом деле, я почувствовал облегчение, увидев ответ, подумал, что схожу с ума.

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

Другое безумие - это одно пространство имен на ерунду сборки.Таким образом, вы получаете SomeBank.SomeApp.Interfaces пространство имен и все в нем.

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

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

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

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

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

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

• Нужен ли когда-либо код вне моей DLLиспользовать мой тип?

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

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

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

• Многие реальные библиотеки и фреймворки являются фирменными, что облегчает ихобсудить, и они не сжигают имена пространств имен, которые подразумевают его использование или являются неоднозначными, могу ли я маркировать компоненты моего приложения, используя «кодовые имена», такие как Steelcore, вместо общих зашифрованных и запутанных терминов, errm «Services»?

Редактировать

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

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

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

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

Library factoring

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

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

Вот и все.

Люк

0 голосов
/ 06 ноября 2018

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

Я получаю сборку для "повторного использования". Но разве мы не можем сделать шаг вперед для нашего конкретного РЕШЕНИЯ?

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

Но для других проектов в рамках НАШЕГО ЖЕ решения, почему бы просто не добавить файл интерфейса с помощью LINK в другие проекты, которым требуется определенный интерфейс?

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

Похоже, лучшее из обоих миров. У нас есть выбор, как получить интерфейс, и он по-прежнему контролируется версией.

Frank

0 голосов
/ 09 января 2013

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

...