我有一个Person类和两个名为Parent和Child的继承类。父母可以有n个孩子,孩子可以有n个父母。
OOD在父母与子女之间创建参考的最佳方式是什么。
我应该在引用连接的父/子的每个类中创建一个List,还是有更好的方法?
答案 0 :(得分:11)
好问题。纯粹的多对多关系实际上非常罕见,它通常有助于引入一个中间对象来模拟关系本身。如果(当!)用例出现需要捕获有关关系的属性(例如,子/父关系是自然的,代理的,收养的等),这将是非常宝贵的。
因此,除了您已经识别的Person,Parent和Child实体之外,我们还要介绍一个名为ParentChildRelationship的对象。 ParentChildRelationship的实例将只引用一个Parent和One Child,Parent和Child类将包含这些实体的集合。
然后,最好确定使用这些实体的用例,并添加适当的辅助方法来维护对象间引用。 在下面的示例中,我刚刚选择将公共AddChild方法添加到父级。
public abstract class Person
{
}
public class Parent : Person
{
private HashSet<ParentChildRelationship> _children =
new HashSet<ParentChildRelationship>();
public virtual IEnumerable<ParentChildRelationship> Children
{
get { return this._children; }
}
public virtual void AddChild(Child child, RelationshipKind relationshipKind)
{
var relationship = new ParentChildRelationship()
{
Parent = this,
Child = child,
RelationshipKind = relationshipKind
};
this._children.Add(relationship);
child.AddParent(relationship);
}
}
public class Child : Person
{
private HashSet<ParentChildRelationship> _parents =
new HashSet<ParentChildRelationship>();
public virtual IEnumerable<ParentChildRelationship> Parents
{
get { return this._parents; }
}
internal virtual void AddParent(ParentChildRelationship relationship)
{
this._parents.Add(relationship);
}
}
public class ParentChildRelationship
{
public virtual Parent Parent { get; protected internal set; }
public virtual Child Child { get; protected internal set; }
public virtual RelationshipKind RelationshipKind { get; set; }
}
public enum RelationshipKind
{
Unknown,
Natural,
Adoptive,
Surrogate,
StepParent
}
答案 1 :(得分:2)
public class Person
{
Person Parent { get;set; }
IList<Person> Children { get;set; }
}
如果您不知道父级,则父级可以为null。 如果您没有孩子,孩子可以为空或空。 由于每个孩子都是一个人,它可以有一个父母或自己的孩子。
在您提供有关如何使用或持久化的更详细的用例场景之前,此设计本身就可以了。
答案 2 :(得分:1)
如果你可以限制关联的方向只能单向,你将省去很多麻烦(但这并不总是可行)。
单向关系:
public class Parent : Person
{
public IEnumerable<Person> Children { get; }
}
如果你想让关联走向另一个方向,你也可以这样做:
public class Child : Person
{
public Parent Parent { get; }
}
但是,现在您需要维护一个循环引用,虽然可能,但它的效率并不高。
您可以通过让孩子提升事件而不是明确引用其父级来将关联保持为单向关系。
答案 3 :(得分:1)
我可以想象一个孩子也可以成为父母(如果他运气好......或者不幸的话,取决于观点),所以我会选择以下内容:
IPerson
{
string Name {get; set;}
string LastName {get; set;}
// whatever else - such as sizeOfShoe, dob, etc
}
IHaveParents
{
// might wanna limit this to a fixed size
List<IPerson> Parents {get; set;}
}
IHaveChildren
{
List<IPerson> Children {get; set;}
}
IHaveSpouse
{
IPerson Spouse {get; set;}
}
public class DudeWithParentsAndChildren : IPerson, IHaveParents, IHaveChildren, IHaveSpouse
{
public void AskMoneyToParents(){throw new Exception("Implement me!");}
public void SlapChildren(){}
private void CheatOnSpouse(){}
// some other stuff that such a dude can do i.e. GoBowling
}
当新的要求出现时,您可以轻松地以任何方式扩展它(相信我会)。
<强>更新强>: 因此,在您的情况下,如果您只想要一个孩子有父母,而另一方面,您可以做类似的事情:
public class Child : IPerson, IHaveParents
{
public void AskMoneyToParents(){throw new Exception("Implement me!");}
}
public class Parent : IPerson, IHaveChildren, IHaveSpouse
{
public void SlapChildren(){}
private void CheatOnSpouse(){}
// some other stuff that such a dude can do i.e. GoBowling
}
这样,如果你想拥有一个IHaveFriends接口,你可以(这基本上迫使实现者将一个IPersons列表公开为名为Friends的属性)。如果你不需要它,那就不要这样做了,但事实上你可以轻松地添加一个接口而其他一切都保持不变意味着你有一个相当不错的可扩展模型(不一定是最好的,你知道我的意思。)
答案 4 :(得分:1)
正如JohnIdol指出的那样,一个孩子可能会成为父母。换句话说,不要创建Person的Parent和Child子类。
class Person
{
readonly List<Person> _children = new List<Person>(),
_parents = new List<Person>();
public IEnumerable<Person> Children
{
get { return _children.AsReadOnly(); }
}
public IEnumerable<Person> Parents
{
get { return _parents.AsReadOnly(); }
}
public void AddChild(Person child)
{
_children.Add(child);
child._parents.Add(this);
}
public void AddParent(Person parent)
{
_parents.Add(parent);
parent._children.Add(this);
}
/* And so on... */
}