Программирование на ориентированные на данные интерфейсы

9

Часть нашего кода написана в следующем стиле:

// IScheduledTask.cs
public interface IScheduledTask
{
    string TaskName { get; set; }
    int TaskPriority { get; set; }
    List<IScheduledTask> Subtasks { get; set; }
    // ... several more properties in this vein
}

// ScheduledTaskImpl.cs
public class ScheduledTaskImpl : IScheduledTask
{
    public string TaskName { get; set; }
    public int TaskPriority { get; set; }
    public List<IScheduledTask> Subtasks { get; set; }
    // ... several more properties in this vein, 
    // perhaps a constructor or two for convenience.
}

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

walpen
источник
1
Одна из причин, по которой я это сделал, - совместимость с COM, но здесь это не похоже на вашу причину.
Майк
@ Майк Интересно, я никогда не использовал COM, и он здесь не используется. Я спрошу автора этого раздела кода, планирует ли он использовать COM или что-то еще.
Walpen
Я предполагаю, что он собирается сделать хотя бы одно из этих свойств неавтоматическим в относительно ближайшем будущем, и выписать этот шаблон в начале - это привычка, которую он приобрел, чтобы сэкономить некоторое время, возясь позже, хотя я не C # парень, так что это все, что я могу сказать.
Ixrec
6
ИМХО, это чрезмерная одержимость и неправильное применение кода к интерфейсам, а не к реализации . Ключевым сигналом является единственная реализация . Смысл интерфейсов - это полиморфизм, но класс, содержащий только свойства, в некотором смысле полиморфен, просто имея разные значения. Даже с поведением - методами - нет смысла в этом, учитывая единственную реализацию. Эта ерунда распространяется по всей нашей кодовой базе и в лучшем случае мешает сопровождению. Я трачу слишком много времени, спрашивая, почему, что и где. Почему они это сделали, какой дизайн я не вижу, и где другие реализации?
Радар Боб
2
Большинство предшествующих комментариев относятся к единой реализации «запах кода» преждевременной абстракции; однако здесь также существует анемичная модель предметного «запаха кода», см. martinfowler.com/bliki/AnemicDomainModel.html
Эрик Эйдт,

Ответы:

5

Вот мои два цента:

  • Мы не уверены, что DTO никогда не будет вести себя хоть немного. Так что для меня есть объекты, которые могут иметь или не иметь поведение в будущем.
  • Одной из возможных причин того, так много классов единственной реализации является отсутствием дизайна , например , не видеть , что ScheduledTask, ScheduledJob, ScheduledEvent, ScheduledInspectionи т.д., должно быть только один отделены Schedulableинтерфейс делает любой реализатор запланированным.
  • Создание интерфейсов - это очень хорошая практика, она дает вам представление о том, что вам нужно. Многие интерфейсы начинаются с отсутствия реализации вообще. Затем кто-то пишет одну реализацию, и у них есть такая. Состояние единственной реализации является временным, если вы избегаете того, что я упоминаю во втором пункте. Как вы узнали заранее, что какой-то интерфейс никогда не будет иметь второй или третьей реализации?
  • Имя, которое мы выбираем для хорошо продуманного интерфейса, имеет тенденцию не изменяться в будущем, поэтому зависимости от этого интерфейса не будут затронуты. С другой стороны, конкретный класс склонен к переименованию, когда что-то важное в способе его реализации изменяется, например, конкретный названный класс TaxSheetможет измениться, SessionAwareTaxSheetпотому что был сделан значительный пересмотр, но интерфейс ITaxSheet, вероятно, не будет так легко переименован.

Нижняя граница:

  • Надлежащая практика проектирования применяется также к DTO.
  • DTO могут быть добавлены немного поведения в будущем.
  • Каждый интерфейс начинается с одной реализации.
  • С другой стороны, если у вас слишком много комбинаций «один интерфейс - один класс», может возникнуть недостаток дизайна, на который следует обратить внимание. Ваша озабоченность может быть оправдана .
Тулаинс Кордова
источник
4
Я проголосовал за ваш ответ, но ваша вторая часть подразумевает, что все классы должны автоматически иметь соответствующий интерфейс, с которым я никогда не соглашался. Написание интерфейсов на случай, если у вас может быть вторая реализация, просто вызывает разрастание интерфейса, и YAGNI.
Роберт Харви
1

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

public interface ISomeDTO { string SomeProperty { get; set; }}

public class SomeDTO : ISomeDTO
{
    public string SomeProperty { get; set; }
    string ISomeDTO.SomeProperty { get { /* different behavior */ } set { SomeProperty = value; } }
}

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

SomeDTO a = GetSomeDTO();
ISomeDTO ia = a;
Assert.IsTrue(a.SomeProperty == ia.SomeProperty); // assert fails!

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

DTO не являются моделями доменов. Мы не должны беспокоиться, если DTO являются анемичными. Процитируем обсуждение Мартином Фаулером модели анемичной области :

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

Erik
источник