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

StackOverflow https://stackoverflow.com/questions/825919

Вопрос

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

Например:

Учетная запись объекта домена:

public class Account : IAccount
{
     public string Name {get;set;}
     //...some more fields that are also in IAccount
     public decimal Balance {get;set;}
}

И это соответствует интерфейсу

public interface IAccount
{
   string Name {get;set;}
   //... all the fields in Account
   decimal Balance {get;set;}
}

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

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

Сначала было заявлено, что эти интерфейсы обеспечивают «развязку», но я против, потому что интерфейсы имеют взаимно-однозначное отношение с объектами домена, что они на самом деле не обеспечивают развязки, что означает изменение интерфейса изменение сущности домена и наоборот.

Следующее утверждение заключается в том, что нам нужны интерфейсы для тестирования. Мой контраргумент состоит в том, что Rhino-mocks обеспечивает насмешку и заглушение конкретных классов. Но они утверждают, что Rhino-mocks имеет проблемы с конкретными классами. Я не знаю, куплю ли я это, даже если у rhino-mocks есть проблемы с конкретными классами, это не обязательно означает, что мы должны использовать интерфейсы для сущностей домена.

Так что мне любопытно:

Почему у вас есть индивидуальные интерфейсы для ваших доменных объектов?

Почему бы и нет?

Почему это хорошая или плохая практика?

Спасибо за чтение!

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

Это было полезно?

Решение

Это плохая практика, как описано, но ...

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

Чтобы использовать ваш пример, интерфейс IAccount, который вы описываете, предоставляет геттеры и сеттеры для объекта Account; кажется немного странным и маловероятным, что все, что использует Учетную запись, будет нуждаться в установке баланса в учетной записи, и что это подразумеваемое разрешение указывается на этом уровне интерфейса. Нет ли в вашей системе места, где вы хотите просто проверить, но не установить остаток на Счете?

Другие советы

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

Но что, если у вас есть, например:

public class Account : IAccount { ... }       // Usual account, persistent
public class MockAccount : IAccount { ... }   // Test mock object
public class TransAccount : IAccount { ... }  // Account, not persistent
public class SimAccount : IAccount { ... }    // Account in a performance sim

и так далее?

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

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

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

interface SomeStrategy {
   void doSomething(StrategyData data);
}

interface StrategyData {
   String getProperty1();

   String getProperty2();
} 

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

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

Интерфейсы «один-к-одному» на объектах являются антишаблоном

Джеймс Грегори выразил это лучше меня здесь .

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

Почему этот провалился?

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

Фундаментальным примером является идентификатор (object.Id) для объекта, он будет отличаться в зависимости от того, где вы храните этот объект, и ответственность за его создание может или не может лежать в реализации данных позже. В SQL Server вы можете использовать автономный номер, но в хранилище таблиц Azure вы можете использовать Guid, но вам не нужно менять бизнес-логику приложения, поскольку вы меняете место хранения своих данных.

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

Мы пошли бы по тому же аргументу о том, каким должен быть адрес, если бы у нас не было IAddress, новые программисты переписывали бы вещи для кредитных карт, если бы не ICreditCard.

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

Для большинства паттернов есть место, даже клеветнический синглтон, что означает, что он не является "анти-паттерном", по крайней мере, как предполагает термин.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top