Хорошо ... после всего обсуждения я немного изменяю свой вопрос, чтобы лучше отразить конкретный пример, с которым я имею дело.
У меня есть два класса ModelOne
и ModelTwo
, эти классы выполняют похожий тип функциональности, но не связаны друг с другом. Тем не менее, у меня есть третий класс, CommonFunc
который содержит некоторые общедоступные функциональные возможности, которые реализованы в обоих ModelOne
и ModelTwo
были учтены в соответствии с DRY
. Две модели создаются внутри ModelMain
класса (который сам создается на более высоком уровне и т. Д., Но я останавливаюсь на этом уровне).
Контейнер IoC, который я использую, - это Microsoft Unity . Я не претендую на то, чтобы быть экспертом в этом, но я понимаю, что вы регистрируете кортеж интерфейса и класса в контейнере, а когда вам нужен конкретный класс, вы спрашиваете у контейнера IoC, какой объект соответствует определенному интерфейсу. Это подразумевает, что для каждого объекта, который я хочу создать из Unity, должен быть соответствующий интерфейс. Поскольку каждый из моих классов выполняет разные (и не перекрывающиеся) функциональные возможности, это означает, что между интерфейсом и классом 1 существует соотношение 1: 1 . Однако это не значит, что я пишу интерфейс для каждого класса, который я пишу.
Таким образом, код мудрый я в конечном итоге с 2 :
public interface ICommonFunc
{
}
public interface IModelOne
{
ICommonFunc Common { get; }
..
}
public interface IModelTwo
{
ICommonFunc Common { get; }
..
}
public interface IModelMain
{
IModelOne One { get; }
IModelTwo Two { get; }
..
}
public class CommonFunc : ICommonFunc { .. }
public class ModelOne : IModelOne { .. }
public class ModelTwo : IModelTwo { .. }
public class ModelMain : IModelMain { .. }
Вопрос в том, как организовать мое решение. Должен ли я держать класс и интерфейс вместе? Или я должен держать классы и интерфейсы вместе? НАПРИМЕР:
Вариант 1 - организованный по имени класса
MySolution
|
|-MyProject
| |
|-Models
| |
|-Common
| |
| |-CommonFunc.cs
| |-ICommonFunc.cs
|
|-Main
| |
| |-IModelMain.cs
| |-ModelMain.cs
|
|-One
| |
| |-IModelOne.cs
| |-ModelOne.cs
|
|-Two
|
|-IModelTwo.cs
|-ModelTwo.cs
|
Вариант 2 - Организован по функциональности (в основном)
MySolution
|
|-MyProject
| |
|-Models
| |
|-Common
| |
| |-CommonFunc.cs
| |-ICommonFunc.cs
|
|-IModelMain.cs
|-IModelOne.cs
|-IModelTwo.cs
|-ModelMain.cs
|-ModelOne.cs
|-ModelTwo.cs
|
Вариант 3 - Разделение интерфейса и реализация
MySolution
|
|-MyProject
|
|-Interfaces
| |
| |-Models
| | |
| |-Common
| | |-ICommonFunc.cs
| |
| |-IModelMain.cs
| |-IModelOne.cs
| |-IModelTwo.cs
|
|-Classes
|
|-Models
| |
|-Common
| |-CommonFunc.cs
|
|-ModelMain.cs
|-ModelOne.cs
|-ModelTwo.cs
|
Вариант 4. Дальнейший пример функциональности
MySolution
|
|-MyProject
| |
|-Models
| |
|-Components
| |
| |-Common
| | |
| | |-CommonFunc.cs
| | |-ICommonFunc.cs
| |
| |-IModelOne.cs
| |-IModelTwo.cs
| |-ModelOne.cs
| |-ModelTwo.cs
|
|-IModelMain.cs
|-ModelMain.cs
|
Мне не нравится вариант 1 из-за имени класса в пути. Но так как я склоняюсь к соотношению 1: 1 из-за моего выбора / использования IoC (и это может быть спорным), у этого есть преимущества в видении отношений между файлами.
Вариант 2 привлекателен для меня, но теперь я замутил воды между ModelMain
и суб-моделями.
Вариант 3 работает, чтобы отделить определение интерфейса от реализации, но теперь у меня есть эти искусственные разрывы в именах путей.
Вариант 4. Я взял вариант 2 и настроил его, чтобы отделить компоненты от родительской модели.
Есть ли веская причина для предпочтения одного над другим? Или какие-то другие потенциальные макеты, которые я пропустил?
1. Фрэнк отметил, что соотношение 1: 1 возвращает нас к дням .h и .cpp файлов C ++. Я знаю, откуда он. Мое понимание Единства, похоже, ставит меня в этот угол, но я также не уверен даже, как выйти из него, если вы также следуете пословице Program to an interface
Но это обсуждение для другого дня.
2. Я опустил детали каждого конструктора объектов. Здесь контейнер IoC вводит объекты по мере необходимости.
источник
Client1
нужноIBase
, это обеспечиваетDerived1
. КогдаClient2
необходимоIBase
, IoC предоставляетDerived2
.interface
для этого. Этоinterface
действительно просто абстрактный класс со всеми виртуальными членами.Ответы:
Поскольку интерфейс абстрактно похож на базовый класс, используйте ту же логику, которую вы использовали бы для базового класса. Классы, реализующие интерфейс, тесно связаны с интерфейсом.
Я сомневаюсь, что вы бы предпочли каталог под названием «Базовые классы»; Большинство разработчиков не хотели бы этого, ни каталог под названием «Интерфейсы». В c # каталоги также являются пространствами имен по умолчанию, что вдвойне запутывает.
Наилучший подход - подумать о том, как бы вы разбили классы / интерфейсы, если бы вам пришлось поместить их в отдельную библиотеку, и аналогичным образом организовать пространства имен / каталоги. В руководствах по разработке .net Framework есть предложения по пространству имен, которые могут оказаться полезными.
источник
Я использую второй подход, конечно же, с еще несколькими папками / пространствами имен в сложных проектах. Это означает, что я удаляю интерфейсы из конкретных реализаций.
В другом проекте вам, возможно, придется знать определение интерфейса, но совсем не обязательно знать какой-либо конкретный класс, реализующий его, особенно когда вы используете контейнер IoC. Таким образом, эти другие проекты должны ссылаться только на проекты интерфейса, а не проекты реализации. Это может удерживать ссылки на низком уровне и может предотвратить проблемы с циклическими ссылками.
источник
Как всегда с любым вопросом дизайна, первое, что нужно сделать, это определить, кто ваши пользователи. Как они собираются использовать вашу организацию?
Они кодеры, которые будут использовать ваши классы? Тогда лучше всего отделить интерфейсы от кода.
Они сопровождают ваш код? Тогда лучше хранить интерфейсы и классы вместе.
Сядьте и создайте несколько вариантов использования. Тогда лучшая организация может предстать перед вами.
источник
Я думаю, что лучше хранить интерфейсы в отдельной библиотеке. Во-первых, такой дизайн увеличивает зависимость. Вот почему реализация этих интерфейсов может быть выполнена другим языком программирования.
источник