消除多重继承

时间:2009-07-15 14:01:24

标签: python oop multiple-inheritance

我有以下问题,我想知道是否有一种不使用多重继承来建模这些对象的好方法。如果它有任何区别,我使用的是Python。

学生需要联系信息和学生信息。成人需要联系信息和结算信息。学生可以是成人学生,在这种情况下我需要联系人/学生/账单信息,或者他们可能是孩子,在这种情况下我需要联系人/学生/家长信息。

为了明确系统的使用方法,我需要能够查询所有成年人的名单(我会得到成年学生和父母),或者所有学生的名单(我会得到的)儿童学生加上成人学生。)

此外,所有这些对象都需要有一个共同的基类。

8 个答案:

答案 0 :(得分:8)

你所拥有的是一个角色的例子 - 它是通过继承对角色进行建模的常见陷阱,但是角色可以改变,并且不建议改变对象的继承结构(即使在可能的语言中,如Python)。儿童成长并成为成年人,一些成年人也将成为儿童学生的父母以及成人学生 - 他们可能会放弃任何一个角色但需要保留另一个角色(他们的孩子会改变学校,但他们没有,或反之亦然)

只有一个具有必填字段和可选字段的Person,而后者代表Roles,可以更改。 “请求列表”(完全独立于继承或其他方式)可以通过动态构建列表(遍历所有对象以检查每个对象是否满足要求)或维护对应于可能要求的列表(或频繁和即席查询的两种策略的混合)。某种类型的数据库可能对此有所帮助(大多数数据库在没有继承的情况下工作得更好; - )。

答案 1 :(得分:5)

我相信其他人很快会发表评论(如果还没有),一个好的OO原则是“Favor composition over inheritance”。从你的描述来看,这听起来很可疑,就像你打破Single Responsibility Principle一样,应该将功能分解为单独的对象。

我也发现Python支持duck typing,这引出了一个问题“为什么所有类都有一个共同的基类这么重要?”

答案 2 :(得分:2)

非常简单的解决方案:使用组合而不是继承。不要让学生继承联系和结算,而是联系Person的字段/属性并从中继承。使计费成为学生的一个领域。让Parent成为Person的自引用字段。

答案 3 :(得分:2)

听起来你真的不需要多重继承。实际上,您并不真正需要多重继承。这只是一个问题,即多重继承是否简化了事情(我在这里看不到这种情况)。

我会创建一个Person类,其中包含成人和学生将共享的所有代码。然后,您可以拥有一个Adult类,其中包含成人需要的所有内容,以及一个仅具有 代码的Child类。

答案 4 :(得分:1)

这听起来像是可以通过组件架构(如zope.components)非常灵活地完成。组件在某种程度上是一种超灵活的组合模式。

在这种情况下,当你加载数据时,我可能最终会做一些事情,根据一些信息设置标记接口,例如,如果你设置了IAdult接口等年龄> = 18,那么你可以得到做成人信息

adultschema = IAdultSchema(person)

或类似的东西。 (编辑:其实我可能会用

queryAdapters(person, ISchema)

一次性获取所有模式。 :)

组件架构可能过度杀伤,但是一旦你习惯这样思考,许多问题就会变得微不足道。 :)

查看Brandons优秀的PyCon谈论它:http://www.youtube.com/watch?v=UF77e2TeeQo 我的介绍博文:http://regebro.wordpress.com/2007/11/16/a-python-component-architecture/

答案 5 :(得分:1)

我认为您的要求过于简化,因为在实际情况下,您可能会让拥有自己帐户的学生处理结算,即使他们是需要父母联系信息的未成年人。此外,您可能在实际情况下将家长联系信息与结算信息不同。您可能还有成年学生与其他人一起开帐单。但是,除了 - 看你的要求,这是一种方式:

类:Person,BillingInfo,StudentInfo。

所有人都是班级人员的实例......

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. 

检查字段将允许您根据需要创建列表。

答案 6 :(得分:0)

一种解决方案是创建ContactInfo,StudentInfo和BillingInfo类继承的基本Info类/接口。有一些包含Info对象列表的Person对象,然后您可以使用ContactInfo,StudentInfo等填充Info对象列表。

答案 7 :(得分: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