2009-07-15 7 views
9

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

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

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

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

+0

Это действительно звучит как домашнее задание. :) –

+0

@Brian MacKay: Возможно, но я сомневаюсь, что это прямое задание на домашнюю работу - то есть «устранить множественное наследование» - но, скорее, часть проекта домашней работы, он пытается найти другой способ приближения. Это, безусловно, лучше спросить, чем некоторые из вопросов о копировании/вставке, которые я видел здесь. – Randolpho

+0

Я это знаю, но могу вас заверить, что это не так :) –

ответ

8

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

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

+0

Является ли причина того, что вы рекомендуете это решение, потому что роли могут измениться? –

+0

Да, изменчивость роли [s] - это мотивация № 1 для избежания наследования в этом случае (есть и другие, которые применяются более широко, и другие ответы упомянуты). –

2

Очень простое решение: используйте композицию, а не наследование. Вместо того, чтобы наследовать Ученика от Контакта и Биллинга, свяжитесь с полем/атрибутом Лица и наследуйте от него. Сделайте Billing поле студента. Сделать родителем поле самореференции Person.

2

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

Я бы создал класс Person, у которого есть весь код, который поделился бы взрослым и студентом. Тогда у вас может быть класс для взрослых, в котором есть все, что только для взрослых потребностей и класс ребенка, который имеет код только ребенок нуждается.

+0

Класс Person был бы огромным. Каждый раз, когда был добавлен новый тип контакта, Person будет расти, и один или несколько других классов-оболочек также будут расти. Алекс Мартелли прав. – Mike

5

Как я уверен, кто-то еще будет комментировать в ближайшее время (если они еще не были), один хороший принцип OO - «Favor composition over inheritance». Из вашего описания это звучит подозрительно, как будто вы нарушаете Single Responsibility Principle и должны разбивать функциональность на отдельные объекты.

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

+0

+1 для связывания композиции предпочтения над наследованием. Именно мой смысл! – Randolpho

+0

Имейте в виду, что, как указано в связанной статье, состав чаще всего реализуется через наследование интерфейса. Если это так, то по-прежнему существует оригинальная проблема. – Mike

0

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

1

Это звучит как нечто, что можно сделать довольно красиво и гибко с помощью архитектуры компонентов, например zope.components. Компоненты в некотором роде являются своего рода супергибкими композиционными структурами.

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

adultschema = IAdultSchema(person) 

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

queryAdapters(person, ISchema) 

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

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

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

1

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

классы: 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. 

Проверка полей позволит вам создавать списки, как вы хотите.

0

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

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 
Смежные вопросы