Должны ли интерфейсы знать об объектах домена? - PullRequest
8 голосов
/ 05 марта 2010

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

Стоит ли сопротивляться и пытаться сохранить интерфейсы независимыми от доменных объектов или это беспочвенная проблема?

РЕДАКТИРОВАТЬ - после публикации вопроса я подумал об определении интерфейса для каждого объекта домена, таким образом, они могут быть отделены друг от друга - не знаю, является ли это излишним или это цена, чтобы они оставались независимыми.

РЕДАКТИРОВАТЬ # 2 - меня попросили привести пример, поэтому я постараюсь сделать его простым. У меня есть процесс, который обрабатывает преобразование изображения, и один из моих доменных объектов - это класс, который содержит такую ​​информацию, как разрешение, список страниц, хэш и т. Д. Пусть он будет называться DocumentInfo. У меня есть класс, который использует DocumentInfo для выполнения таких действий, как GeneratePdfFromTiff; Сначала я определил GeneratePdfFromTiff как метод интерфейса IImageHandler, который затем реализуется ImageHandler.

Проблема в том, что одним из параметров для GeneratePdfFromTiff является DocumentInfo. Здесь у меня есть метод, определенный на уровне интерфейса, который должен знать о DocumentInfo, который был определен на уровне домена. Это та зависимость, о которой я беспокоюсь.

Ответы [ 4 ]

3 голосов
/ 05 марта 2010

Это сложный вопрос, я попробую.+1 за вопрос.

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

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

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

Из того, что я знаю, интерфейсы в основном (всегда?) Общедоступны, что вызывает проблемы с доступом.Интерфейс не должен раскрывать подробности о реализации, но классы модели предметной области содержат фактическую реализацию.Таким образом, вместо «просто доступа к некоторому значению» вам придется создать еще один механизм абстракции для каждой детали.Вы не можете, например, получить доступ к любому идентификатору, пользователю и т. Д. В системе.Вам понадобится абстрактный тип идентификатора, поставщик идентификаторов и т. Д. Я считаю, что это может быть чрезвычайно громоздким в большой реальной модели.

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

Боюсь, это не слишком удовлетворительно, но, похоже, по сути это тонкая линия.

2 голосов
/ 05 марта 2010

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

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

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

1 голос
/ 05 марта 2010

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

Если GeneratePdfFromTiff просто обрабатывает DocumentInfo как объект параметра и класс информации документа не имеет никакой логики, он должен быть просто общедоступным типом.

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

1 голос
/ 05 марта 2010

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

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

Я бы настоятельно рекомендовал вам извлекать интерфейсы из тех доменных объектов, которые вы хотели бы использовать вместе с вашими интерфейсами.

Если у вас уже есть класс с именем Foo, извлеките из него интерфейс IFoo и используйте его в других ваших интерфейсах:

public interface IMyService
{
    public IBar DoStuff(IFoo foo);
}

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

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

Если Foo выглядит так

public class Foo
{
    public Baz Baz { get; }
}

интерфейс должен выглядеть так:

public interface IFoo
{
    IBaz Baz { get; }
}

public interface IBaz {}

(извинения за код C #, если вы пишете на другом языке ...)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...