Устранение множественного наследования

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

Студентам нужна контактная информация, а также информация о студенте. Взрослым нужна контактная информация, а также платежная информация. Учащиеся могут быть взрослыми студентами, и в этом случае мне нужна контактная информация/информация о студенте/платежеспособности, или они могут быть детьми, и в этом случае мне нужна контактная информация/информация о студенте/родителе.

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

Кроме того, все эти объекты должны иметь общий базовый класс.


person Mike J    schedule 15.07.2009    source источник
comment
Это действительно похоже на домашнее задание. :)   -  person Brian MacKay    schedule 15.07.2009
comment
@Brian MacKay: Возможно, но я сомневаюсь, что это прямое домашнее задание, то есть устранение множественного наследования, но, скорее всего, это часть какого-то домашнего задания, к которому он пытается найти другой подход. Это, безусловно, лучше задать, чем некоторые домашние вопросы о копировании / вставке, которые я видел здесь.   -  person Randolpho    schedule 15.07.2009
comment
Я знаю, что это так, но я могу заверить вас, что это не так :)   -  person Mike J    schedule 15.07.2009
comment
Это именно та проблема, над которой я работаю на этой неделе! Детали, конечно, очень разные. Конечно, это не домашнее задание. Мое приложение — это обработка научных чисел.   -  person DarenW    schedule 25.01.2011


Ответы (8)


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

Просто есть класс Person с обязательными полями и необязательными, причем последние, представляющие роли, могут меняться. «Запрашивать список» (совершенно независимо от наследования или иным образом) можно либо путем создания списка «на лету» (проходя по всем объектам, чтобы проверить соответствие каждого из них требованиям), либо поддерживая списки, соответствующие возможным требованиям (или сочетание двух стратегий как для частых, так и для специальных запросов). Здесь может помочь какая-то база данных (и большинство БД работают намного лучше без наследования;-).

person Alex Martelli    schedule 15.07.2009
comment
Причина, по которой вы рекомендуете это решение, заключается в том, что роли могут меняться? - person Mike J; 15.07.2009
comment
Да, изменчивость роли [s] - это мотивация № 1 для предотвращения наследования в этом случае (есть и другие, которые применяются более широко, и другие ответы упоминались). - person Alex Martelli; 15.07.2009

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

Мне также приходит в голову, что Python поддерживает утиный ввод, что вызывает вопрос: "Почему это так?" важно, чтобы все классы имели общий базовый класс?»

person Richard J Foster    schedule 15.07.2009
comment
+1 за привязку предпочтительной композиции к наследованию. Точно моя точка зрения! - person Randolpho; 15.07.2009
comment
Имейте в виду, что, как указано в связанной статье, композиция чаще всего реализуется через наследование интерфейса. Если это так, то исходная проблема все еще существует. - person Mike; 15.07.2009

Очень простое решение: используйте композицию, а не наследование. Вместо того, чтобы наследовать Student от Contact и Billing, сделайте Contact полем/атрибутом Person и наследуйте от него. Сделайте Billing полем Student. Сделайте Parent полем ссылки на самого себя для Person.

person Randolpho    schedule 15.07.2009

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

Я бы создал класс Person со всем кодом, которым поделились бы взрослый и ученик. Затем у вас может быть класс Adult со всем, что только нужно взрослому, и класс Child с кодом только, который нужен ребенку.

person Jason Baker    schedule 15.07.2009
comment
Класс Person будет огромным. Каждый раз, когда добавлялся новый тип контакта, Person рос, а также рос один или несколько других классов-оболочек. Алекс Мартелли прав. - person Mike; 15.07.2009

Звучит так, как будто это можно сделать очень красиво и гибко с компонентной архитектурой, такой как zope.components. Компоненты — это своего рода сверхгибкие шаблоны композиции.

В этом случае я, вероятно, в конечном итоге сделаю что-то, когда вы загружаете данные, чтобы также установить для них интерфейсы маркеров в зависимости от некоторой информации, например, если возраст> = 18, вы устанавливаете интерфейс IAdult и т. д. Затем вы можете получить информацию для взрослых с помощью делает

adultschema = IAdultSchema(person)

или что-то вроде того. (Редактировать: на самом деле я бы, вероятно, использовал

queryAdapters(person, ISchema)

чтобы получить все схемы за один раз. :)

Компонентная архитектура может быть излишней, но как только вы привыкнете к такому мышлению, многие проблемы станут тривиальными. :)

Посмотрите отличный доклад Брэндона на PyCon: http://www.youtube.com/watch?v=UF77e2TeeQo И мой вступительный пост в блоге: http://regebro.wordpress.com/2007/11/16/a-python-component-architecture/

person Lennart Regebro    schedule 15.07.2009

Я думаю, что ваши требования чрезмерно упрощены, поскольку в реальной ситуации у вас могут быть учащиеся со своими учетными записями для выставления счетов, даже если они несовершеннолетние, которым нужна контактная информация родителей. Кроме того, в реальной ситуации контактная информация родителей может отличаться от платежной информации. У вас также могут быть взрослые учащиеся с кем-то еще, чтобы выставить счет. НО, кроме того, глядя на ваши требования, вот один из способов:

классы: Person, BillingInfo, StudentInfo.

Все люди являются экземплярами класса Person...

class Person:
    # Will have contact fields all people have - or you could split these off into an
    # object.
    parent        # Will be set to None for adults or else point to their parent's
                  # Person object.
    billing_info  # Set to None for non-adults, else to their BillingInfo object.
    student_info  # Set to None for non-student parents, else to their StudentInfo
                  # object. 

Проверка полей позволит вам создавать списки по своему желанию.

person Anon    schedule 15.07.2009

Одним из решений является создание базового класса/интерфейса Info, от которого наследуются классы ContactInfo, StudentInfo и BillingInfo. Имейте какой-то объект Person, который содержит список объектов Info, а затем вы можете заполнить список объектов Info с помощью ContactInfo, StudentInfo и т. д.

person Ski    schedule 15.07.2009

В псевдокоде вы могли бы сделать что-то вроде этого:

Class Student
    Inherits WhateverBase

    Private m_StudentType as EnumStudentTypes 'an enum containing: Adult, Child
    Private m_Billing as Billing
    Private m_Contact as Contact
    Private m_Parent as Parent

    Public Sub Constructor(studentType, billing, contact, parent)
        ...logic to make sure we have the right combination depending on studentType.
        ...throw an exception if we try to assign a a parent to an adult, etc.
        ...maybe you could have seperate constructors, one for each studenttype.             
    End Sub


    Public Property StudentType as EnumStudentTypes
        Get
             Return m_StudentType
        End Get
    End Sub

    Public Property Parent 
        Get 
           ...code to make sure we're using a studentType that has a parent,
           ...and throws an exception if not. Otherwise it returns m_Parent
        End Get
    End Sub


    [more properties]
End Class Student

Затем вы можете создать класс с именем StudentManager:

Public Class StudentManager
    Public Function GetAdults(studentCollection(Of Students)) as StudentCollection(Of Students)
        Dim ResultCollection(Of Students)

        ...Loop through studentCollection, adding all students where Student.StudentType=Adult  

        Return ResultCollection
    End Function


    [Other Functions]
End Class

Public Enum StudentType
    Adult=0
    Child=1  
End Enum
person Brian MacKay    schedule 15.07.2009