我有以下问题,我想知道是否有一种不使用多重继承来建模这些对象的好方法。如果它有任何区别,我使用的是Python。
学生需要联系信息和学生信息。成人需要联系信息和结算信息。学生可以是成人学生,在这种情况下我需要联系人/学生/账单信息,或者他们可能是孩子,在这种情况下我需要联系人/学生/家长信息。
为了明确系统的使用方法,我需要能够查询所有成年人的名单(我会得到成年学生和父母),或者所有学生的名单(我会得到的)儿童学生加上成人学生。)
此外,所有这些对象都需要有一个共同的基类。
答案 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