Свойства наследования и перечисления в доменной модели - PullRequest
33 голосов
/ 23 ноября 2010

У меня была дискуссия на тему: «Наследование в доменной модели усложняет жизнь разработчикам».Я - программист OO, поэтому я начал искать аргументы в пользу того, что наличие наследования в доменной модели на самом деле облегчит жизнь разработчика, а не повсеместно.

Я хотел бы видеть следующее:

class Animal {

}

class Cat : Animal {

}

class Dog : Animal {

}

Что говорит другой коллега:

public enum AnimalType {
    Unknown,
    Cat,
    Dog
}

public class Animal {

    public AnimalType Type { get; set; }

}

Как мне убедить его (ссылки WELCOME ), что иерархия классов будет лучше, чемсвойство enum для подобных ситуаций?

Спасибо!

Ответы [ 6 ]

25 голосов
/ 23 ноября 2010

Вот как я рассуждаю об этом:

Используйте наследование только в том случае, если роль / тип никогда не изменится. например

используя наследование для таких вещей, как:

Пожарный <- Сотрудник <- Человек не прав. </p>

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

Таким образом, наивным решением вышеуказанной проблемы было бы присвоить свойству enum свойство JobTitle. Этого может быть достаточно в некоторых сценариях, например, если вам не нужно очень сложное поведение, связанное с ролью / типом.

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

, например

freddy.Roles.Add(new Employement( employmentDate, jobTitle ));

или, если это излишне:

freddy.CurrentEmployment = new Employement( employmentDate, jobTitle );

Таким образом, Фредди может стать разработчиком без необходимости сначала его убивать.

Тем не менее, все мои сообщения все еще не отвечают, если вы должны использовать перечисление или иерархию типов для названия работы.

В чистом виде в mem OO я бы сказал, что здесь правильнее использовать наследование для рабочих титулов.

Но если вы выполняете O / R-сопоставление, вы можете получить немного сверхсложную модель данных за кулисами, если mapper попытается отобразить каждый подтип в новую таблицу. Поэтому в таких случаях я часто использую подход enum, если с типами не связано реальное / сложное поведение. Я могу жить с «если тип == JobTitles.Fireman ...», если использование ограничено, и это делает вещи проще и менее сложным.

например. конструктор Entity Framework 4 для .NET может сопоставлять только каждый подтип с новой таблицей. и вы можете получить некрасивую модель или множество объединений при запросе к базе данных без какой-либо реальной выгоды.

Однако я использую наследование, если тип / роль статические. например для продуктов.

у вас может быть компакт-диск <- продукт и книга <- продукт. Наследование выигрывает здесь, потому что в этом случае у вас скорее всего будет другое состояние, связанное с типами. CD может иметь свойство количества дорожек, в то время как книга может иметь свойство количества страниц. </p>

Короче, все зависит; -)

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

if (продукт Book) Response.Redicted ("~ / EditBook.aspx? Id" + product.id);

Поскольку кодирование URL-адреса книги редактирования в классе сущностей было бы ужасно, так как это заставило бы ваших деловых людей узнать о структуре вашего сайта и т. Д.

7 голосов
/ 23 ноября 2010

Я призываю вас пересмотреть: в модели анемичной области (согласно комментариям выше) кошки не ведут себя иначе, чем собаки, поэтому полиморфизма нет.Тип животного на самом деле просто атрибут.Трудно увидеть, что наследство покупает там.

7 голосов
/ 23 ноября 2010

Перечисления хороши, когда:

  1. Набор значений фиксирован и никогда или очень редко изменяется.
  2. Вы хотите иметь возможность представлять объединение значений (т. Е. Объединять флаги).
  3. Вам не нужно прикреплять другое состояние к каждому значению. (У Java нет этого ограничения.)

Если бы вы могли решить свою проблему с помощью числа, перечисление, скорее всего, подходит и более безопасно для типов. Если вам нужно больше гибкости, чем указано выше, тогда перечисления, скорее всего, не правильный ответ. Используя полиморфные классы, вы можете:

  1. Статически убедитесь, что все специфичные для типа поведения обрабатываются. Например, если вам нужно, чтобы все животные были в состоянии Bark(), создание классов Animal с абстрактным методом Bark() позволит компилятору проверить, что каждый подкласс реализует его. Если вы используете enum и большое switch, это не гарантирует, что вы обработали каждый случай.

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

Важно отметить, что пример вашего коллеги не прямо противоположен вашему. Если он хочет, чтобы тип животного являлся экспонируемым свойством (что полезно для некоторых вещей), вы все равно можете сделать это без использования enum, используя шаблон объекта типа :

public abstract class AnimalType {
    public static AnimalType Unknown { get; private set; }
    public static AnimalType Cat { get; private set; }
    public static AnimalType Dog { get; private set; }

    static AnimalType() {
        Unknown = new AnimalType("Unknown");
        Cat = new AnimalType("Cat");
        Dog = new AnimalType("Dog");
    }
}

public class Animal {
    public AnimalType Type { get; set; }
}

Это дает вам удобство перечисления: вы можете сделать AnimalType.Cat, и вы можете получить тип животного. Но это также дает вам гибкость классов: вы можете добавлять поля в AnimalType для хранения дополнительных данных с каждым типом, добавлять виртуальные методы и т. Д. Более того, вы можете определять новые типы животных, просто создавая новые экземпляры AnimalType .

5 голосов
/ 09 мая 2011

Перечисление - это все равно что устраивать вечеринку для всех этих Open/Closed Principle is for suckers людей.

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

1 голос
/ 03 мая 2011

Оба решения верны. Вы должны посмотреть, какие методы лучше подходят для вашей проблемы.

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

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

1 голос
/ 23 ноября 2010

Самое главное OOPS означает моделирование реальности.Наследование дает вам возможность сказать, что кошка - животное.Животное не должно знать, кричит ли его кошка, а затем решает, что это Мяу, а не Барк, Инкапсуляция там побеждена.Меньше кода, как сейчас вы не должны делать Если еще, как вы сказали.

...