2010-11-23 2 views
29

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

То, что я хотел бы видеть это:

class Animal { 

} 

class Cat : Animal { 

} 

class Dog : Animal { 

} 

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

public enum AnimalType { 
    Unknown, 
    Cat, 
    Dog 
} 

public class Animal { 

    public AnimalType Type { get; set; } 

} 

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

Спасибо!

+3

Это для объектной модели в памяти или ORM? Я не считаю пример «Животные» очень полезным, так как он не выполняет многие общие проблемы, с которыми вы сталкиваетесь при выполнении реального дизайна OO. – 2010-11-23 08:54:37

+0

@Marcelo: согласовано. В частности, какой угол поведения? – sfinnie 2010-11-23 09:04:15

+0

Собаки коры, кошки мяу, используя реализацию вашего коллеги, такое поведение будет сложно реализовать на C# (например). Он по-прежнему полезен в функциональных языках, таких как F #, где дискриминационные союзы являются очень мощным средством обеспечения полиморфизма. – 2010-11-23 09:32:39

ответ

24

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

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

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

< Fireman - Сотрудник < - Человек неправильно.

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

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

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

например.

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

или, если это является излишеством:

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

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

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

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

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

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

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

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

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

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

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

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

1

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

6

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

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

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

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

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

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

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.

7

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

1

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

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

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

5

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

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

Смежные вопросы