Моделирование (и отображение) иерархии классов с двумя степенями полиморфизма? - PullRequest
1 голос
/ 24 февраля 2012

Я сталкиваюсь с ситуацией, когда у меня есть два уровня полиморфизма, один внутри другого, в иерархии родитель / потомок.

Я думаю, это лучше всего объяснить на простом примере:

    class Group
    {
        public IList<Person> People { get; set; }
    }

    class SpecialGroup : Group
    {
        public IList<SpecialPerson> People { get; set; }
    }

    class Person {}

    class SpecialPerson : Person {}

Таким образом, у Группы есть список объектов Person, тогда как у специализированной группы (SpecialGroup) есть специализированный список объектов Person (SpecialPerson).

Это компилируется, но я получаю предупреждение о том, что я должен использовать ключевое слово "new" в SpecialGroup.People, потому что оно скрывает свойство Group.People.

Я понимаю, что это значит, но, возможно, я не до конца понимаю, как вы могли бы правильно смоделировать что-то подобное в C # для начала. Любые мысли о том, как смоделировать это лучше?

Кроме того, есть идеи, как это будет играть с NHibernate? Я не уверен, что использование ключевого слова "new" обрежет его, поскольку свойство Group.People уже будет отображаться с другим типом. Есть ли лучший способ смоделировать это, совместимый с NH?


Хорошо, я подумал о том, как смоделировать это с помощью обобщений:

    abstract class AbstractGroup<PersonType>
        where PersonType : Person
    {
        public IList<PersonType> People { get; set; }
    }

    class Group : AbstractGroup<Person>
    {}

    class SpecialGroup : AbstractGroup<SpecialPerson>
    {}

    class Person {}

    class SpecialPerson : Person {}

Я думаю, это делает то, что я хочу? Теперь Person может делиться чертами с SpecialPerson, но только SpecialPerson может быть добавлен в SpecialGroup. Базовый тип AbstractGroup может моделировать черты, общие для любой группы.

Теперь вопрос, взорвется ли NH, если я попытаюсь отобразить это?


Для записи, похоже, что некоторые люди успешно использовали запрос с универсальными классами - для запросов HQL (мой сценарий использования) это не поддерживается.

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

NHibernate: отображение таблицы на подкласс и новое ключевое слово

Заключение на данный момент: проверка во время выполнения и исключения, как предложено @empi ниже.

Ответы [ 2 ]

2 голосов
/ 25 февраля 2012

Этот случай также называется "параллельным" или "двойным" наследованием.

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

Хотя и не обязательно, лучше иметь «общий» или «абстрактный» класс каждого класса, который должен быть переопределен, но уже имеет понятие зависимости.

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

В данном конкретном случае обычно лучше отображать только первый уровень :

............................................................
....+----------------+................+----------------+....
....|  <<Abstract>>  |..<<contains>>..|  <<Abstract>>  |....
....| AbstractGroup  +<>--------------+ AbstractPerson |....
....|                |................|                |....
....+----------------+................+----------------+....
............................................................

Хотя вы можете смоделировать несколько уровней, это не рекомендуется :

............................................................
....+----------------+................+----------------+....
....|  <<Abstract>>  |..<<contains>>..|  <<Abstract>>  |....
....| AbstractGroup  +<>--------------+ AbstractPerson |....
....|                |................|                |....
....+-------+--------+................+--------+-------+....
............|..................................|............
............|..................................|............
............^..................................^............
............|..................................|............
............|..................................|............
....+-------+--------+................+--------+-------+....
....|  <<Concrete>>  |..<<contains>>..|  <<Concrete>>  |....
....|      Group     +<>--------------+     Person     |....
....|                |................|                |....
....+----------------+................+----------------+....
............|..................................|............
............|..................................|............
............^..................................^............
............|..................................|............
............|..................................|............
....+-------+--------+................+--------+-------+....
....|  <<Concrete>>  |..<<contains>>..|  <<Concrete>>  |....
....|   SchoolGroup  +<>--------------+     Student    |....
....|                |................|                |....
....+----------------+................+----------------+....
............................................................

Вы упомянули NHibernate, представляют ли ваши классы данные для хранения?

Некоторые похожие вопросы:

Избежание параллельных иерархий наследования

Рефакторинг параллельной иерархии наследования

Как избежать параллельных иерархий наследования между элементами управления GUI и объектами домена

Параллельное наследование между интерфейсными классами и классами реализации в C ++

Приветствие.

2 голосов
/ 24 февраля 2012

Если вы хотите применить правило, согласно которому SpecialGroup содержит только SpecialPerson, вы можете сделать это программно, не затеняя свойство People. Я предполагаю, что у вас есть какой-то метод AddPerson. Вы можете переопределить его в SpecialGroup и выбросить исключение, если кто-то попытается добавить запись, которая не SpecialPerson. Если вы хотите получить список SpecialPerson, вы можете добавить метод IEnumerable<SpecialPerson> GetSpecialPeople() и привести Person к SpecialPerson (это всегда будет действительное приведение, так как вы проверяете тип в методе AddPerson). Делая это, вам не нужно скрывать свойство People, и NHibernate будет работать нормально.

...