Подтвердить что ты не робот

Наследование и свойства enum в модели домена

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

Что бы я хотел увидеть, это:

class Animal {

}

class Cat : Animal {

}

class Dog : Animal {

}

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

public enum AnimalType {
    Unknown,
    Cat,
    Dog
}

public class Animal {

    public AnimalType Type { get; set; }

}

Как убедить его (ссылки ДОБРО ПОЖАЛУЙСТА), что иерархия классов будет лучше, чем наличие свойства enum для таких ситуаций?

Спасибо!

4b9b3361

Ответ 1

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

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

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

Fireman < - Employee < - Лицо ошибочно.

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

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

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

например.

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

или если это перебор:

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

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

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

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

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

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

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

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

Итак, это зависит от: -)

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

если (продукт - книга)       Response.Redicted( "~/EditBook.aspx? Id" + product.id);

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

Ответ 2

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

Ответ 3

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

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

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

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

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

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

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.

Ответ 4

Наличие переименования - это как разбрасывание вечеринки для всех тех людей Open/Closed Principle is for suckers.

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

Ответ 5

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

Ответ 6

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

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

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