В какое пространство имен следует поместить интерфейсы относительно их реализаторов?
в частности, когда вы создаете пару интерфейс / реализатор, и нет переопределяющей организационной проблемы (например, интерфейс должен идти в другой сборке ie, как рекомендовано архитектурой s#), у вас есть способ по умолчанию организовать их в вашей схеме пространства имен/именования?
Это, очевидно, более основанный на мнении вопрос, но я думаю, что некоторые люди думали об этом больше, и мы все можем извлечь выгоду из их выводов.
9 ответов
ответ зависит от ваших намерений.
- Если вы хотите, чтобы потребитель ваших пространств имен использовал интерфейсы над конкретными реализациями, я бы рекомендовал иметь ваши интерфейсы в пространстве имен верхнего уровня с реализациями в дочернем пространстве имен
- если потребитель должен использовать оба, имейте их в одном пространстве имен.
- Если интерфейс предназначен для преимущественно специализированного использования, например, для создания новых реализаций, рассмотрите возможность их в дочернем пространстве имен, таком как Design или ComponentModel.
Я уверен, что есть и другие варианты, но, как и в большинстве проблем с пространством имен, это сводится к прецедентам проекта, а также классам и интерфейсам, которые он содержит.
Я обычно сохраняю интерфейс в том же пространстве имен, что и конкретные типы.
но это только мое мнение, а макет пространства имен очень субъективен.
Animals
|
| - IAnimal
| - Dog
| - Cat
Plants
|
| - IPlant
| - Cactus
вы ничего не получаете, перемещая один или два типа из основного пространства имен, но вы добавляете требование для одного дополнительного оператора using.
обычно я создаю пространство имен Interfaces на высоком уровне в моей иерархии и помещаю туда все интерфейсы (я не утруждаю себя вложением других пространств имен, поскольку тогда у меня будет много пространств имен, содержащих только один интерфейс).
Interfaces
|--IAnimal
|--IVegetable
|--IMineral
MineralImplementor
Organisms
|--AnimalImplementor
|--VegetableImplementor
Я предпочитаю держать мои интерфейсы и классы реализации в одном пространстве имен. Когда это возможно, я даю классам реализации внутреннюю видимость и предоставляю фабрику (обычно в виде статического фабричного метода, который делегирует рабочему классу, с внутренним методом, который позволяет модульным тестам в дружественной сборке заменить другого работника, который производит заглушки). Конечно, если конкретный класс должен быть общедоступным-например, если это абстрактный базовый класс, то это нормально; я не вижу причин помещать ABC в собственное пространство имен.
на боковой ноте мне очень не нравится .NET-соглашение о предварении имен интерфейсов буквой "i". дело в том, что (I)модели интерфейса Foo не являются ifoo, просто фу. Так почему я не могу просто назвать это фу? Затем я называю классы реализации конкретно, например, AbstractFoo, MemoryOptimizedFoo, SimpleFoo, StubFoo и т. д.
(.Net) я склонен хранить интерфейсы в отдельной "общей" сборке, поэтому я могу использовать этот интерфейс в нескольких приложениях и, чаще всего, в серверных компонентах моих приложений.
по поводу имен, я держу их в BusinessCommon.Межфазные границы.
Я делаю это, чтобы убедиться, что ни я, ни мои разработчики не склонны ссылаться на реализации напрямую.
Я ненавижу, когда нахожу интерфейсы и реализации в одном пространстве имен/сборке. Пожалуйста, не делайте этого, если проект развивается, это боль в заднице для рефакторинга.
когда я ссылаюсь на интерфейс, я хочу реализовать его, а не получить все его реализации.
что может быть допустимым, это поместить интерфейс с его классом зависимостей (класс, который ссылается на интерфейс).
EDIT: @Josh, я только что прочитал последнее предложение мой, это сбивает с толку! конечно, и класс зависимостей, и тот, который его реализует, ссылаются на интерфейс. Для ясности приведу примеры:--4-->
приемлемо :
интерфейс + реализация :
namespace A;
Interface IMyInterface
{
void MyMethod();
}
namespace A;
Interface MyDependentClass
{
private IMyInterface inject;
public MyDependentClass(IMyInterface inject)
{
this.inject = inject;
}
public void DoJob()
{
//Bla bla
inject.MyMethod();
}
}
класс, реализующий:
namespace B;
Interface MyImplementing : IMyInterface
{
public void MyMethod()
{
Console.WriteLine("hello world");
}
}
НЕПРИЕМЛЕМО:
namespace A;
Interface IMyInterface
{
void MyMethod();
}
namespace A;
Interface MyImplementing : IMyInterface
{
public void MyMethod()
{
Console.WriteLine("hello world");
}
}
и, пожалуйста, не создавайте проект/мусор для ваших интерфейсов ! пример: ShittyProject.Межфазные границы. Вы упустили главное!
представьте, что вы создали DLL, зарезервированную для ваших интерфейсов (200 МБ). Если вам нужно было добавить один интерфейс с двумя строками кодов, вашим пользователям придется обновить 200 МБ только для двух тупых сигнатур!
разделите интерфейсы каким-то образом (проекты в Eclipse и т. д.), Чтобы было легко развернуть только интерфейсы. Это позволяет предоставлять внешний API без предоставления реализаций. Это позволяет зависимым проектам строить с минимальным количеством внешних компонентов. Очевидно, что это больше относится к более крупным проектам, но концепция хороша во всех случаях.
обычно я разделяю их на две отдельные сборки. Одной из обычных причин для интерфейса является то, что ряд объектов выглядит одинаково для некоторой подсистемы вашего программного обеспечения. Например, у меня есть все мои отчеты, реализующие интерфейсы IReport. IReport используется не только для печати, но и для предварительного просмотра и выбора отдельных параметров для каждого отчета. Наконец, у меня есть коллекция IReport для использования в диалоговом окне, где пользователь выбирает, какие отчеты (и параметры настройки) они хочу напечатать.
отчеты находятся в отдельной сборке, а параметры IReport, Preview engine, print engine, report находятся в соответствующей основной сборке и/или сборке пользовательского интерфейса.
Если вы используете класс Factory для возврата списка доступных отчетов в сборке отчета, обновление программного обеспечения новым отчетом становится просто вопросом копирования новой сборки отчета поверх оригинала. Вы даже можете использовать API отражения, чтобы просто сканировать список сборки для любых фабрик отчетов и создайте свой список отчетов таким образом.
вы также можете применить эти методы к файлам. Мое собственное программное обеспечение работает на металлорежущем станке, поэтому мы используем эту идею для библиотек формы и подгонки, которые мы продаем вместе с нашим программным обеспечением.
снова классы, реализующие основной интерфейс, должны находиться в отдельной сборке, чтобы вы могли обновлять ее отдельно от остального программного обеспечения.
Я даю свой собственный опыт, который противоречит другим ответам.
Я склонен помещать все мои интерфейсы в пакет, к которому они принадлежат. Это означает, что, если я перемещаю пакет в другой проект, у меня есть все, что нужно, чтобы запустить пакет без каких-либо изменений.
для меня любые вспомогательные функции и функции оператора, которые являются частью функциональности класса, должны входить в то же пространство имен, что и у класса, потому что они являются частью открытого API этого пространство имен.
Если у вас есть общие реализаций, которые разделяют тот же самый интерфейс в разных пакетах, возможно, потребуется рефакторинг проекта.
иногда я вижу, что в проекте есть много интерфейсов, которые могут быть преобразованы в абстрактную реализацию, а не в интерфейс.
Итак, спросите себя, действительно ли вы моделируете тип или структуру.