双亲关系亲子关系的设计

时间:2010-07-20 10:49:55

标签: c# data-structures linked-list

我已经定义了一个C#类,它应该是有向图的元素(基本上是一个树,但是一个元素可以有多个父元素 - 我不知道是否有一个特殊的名称)。

每个元素现在应该是所有孩子及其所有父母。它将这些列表公开为IEnumarable

public interface IMyClass
{
  public IEnumerable<MyClass> Children { get; }
  public IEnumerable<MyClass> Parents { get; }
}

public class MyClass : IMyClass
{
  private List<MyClass> _parents;
  private List<MyClass> _children;

  public IEnumerable<MyClass> Children
  {
    get { foreach (var child in _children) yield return child; }
  }

  public IEnumerable<MyClass> Parents
  {
    get { foreach (var parent in _parents) yield return parent; }
  }

在给定元素添加或删除子元素时,我想确保将给定元素添加或删除到子元素的父元素列表中。

第一个想法是仅公开AddChild(MyClass theChild)方法。问题是,我无法将父项添加到theChild对象,因为它没有公开AddParent(父)方法,我也无法调用theChild-object的私有方法。

所以我试着去暴露一个AddParent(MyClass theParent)方法。为了确保两个对象链接都已设置,我的第一个镜头是在另一个函数中调用AddParent / AddChild,如下所示:

public void AddChild(IMyClass theChild)
{
  _children.Add(theChild);
  theChild.AddParent(this);
}

public void AddParent(IMyClass theParent)
{
  _parent.Add(theParent);
  theParent.AddChild(this);
}

但显然,这是一个致命的循环。

更糟糕的是,我想允许一个元素可以多次成为另一个元素的子元素(这不是一个要求,但我想确保,当这个要求出现时,我的代码不需要被触动。)

是否有任何算法/标准方法可以确保在向父母添加子项时始终设置两个对象链接?

提前致谢,
弗兰克

Edith:添加了用于更正示例的接口。

4 个答案:

答案 0 :(得分:2)

你可以这样做:

public void AddChild(MyClass theChild)
{
    _children.Add(theChild);
    theChild._parent.Add(this);
}

public void AddParent(MyClass theParent)
{
    _parent.Add(theParent);
    theParent._children.Add(this);
}

应该没有问题;只是因为你所指的是你正在编写方法的类的不同实例并不意味着你无法访问它的成员(即使它们是私有的)。


确定使用修改后的代码,您可以在界面中添加两个新成员:NotifyChildAddedNotifyParentAdded,并执行如下操作:

public void AddChild(MyClass theChild)
{
    _children.Add(theChild);
    theChild.NotifyParentAdded(this);
}

public void AddParent(MyClass theParent)
{
    _parent.Add(theParent);
    theParent.NotifyChildAdded(this);
}

public void NotifyChildAdded(MyClass theChild)
{
    _children.Add(theChild);
}

public void NotifyParentAdded(MyClass theParent)
{
    _parent.Add(theParent);
}

希望有所帮助!

答案 1 :(得分:0)

...但我想确保,当这个要求出现时,我的代码无需触及。
这明显违反了YAGNI原则(you ain't gonna need it)。也许您应该立即开始以您需要的方式对其进行编码(这意味着使用Contains() ^^)。当他们来时,关心你需求的变化......

答案 2 :(得分:0)

一个选项是将列表属性公开为内部属性,并使用公共添加方法调用访问另一个类的内部属性。

另一个选择是拥有哑数据和单独的业务逻辑层,这是外部代码调用的。

答案 3 :(得分:0)

另一种可能的设计是存储Graph对象中节点之间的关系,而不是将此信息嵌入Node对象中。如果您仍想使用Node类进行操作,可以将Graph的接口传递给节点,以便它们可以提供“方便”功能,如GetParents(),GetChildren(),AddParent(),AddChild(),它们可以调用适当的公共Graph函数。这里的想法是节点不应该负责管理Graph操作;这应该是图表的责任。

一个简单的实现是邻接矩阵。这是一个NxN布尔数组,其中N是节点数,A [i,j]的内容表示是否存在从节点i到节点j的链接。这使得更新父/子关系O(1)(简单地设置/重置标志)并使得枚举父节点和子节点给定节点O(N)。缺点是你使用O(N ^ 2)空间。如果您想频繁改变图形,这是一种很好的方法。如果存储权重而不是标记,它也会推广到加权图。

如果您想节省空间,可以使用“列表列表”类型的方法。如果链接数远小于节点数(即图形稀疏连接)并且节点数量很大,这实际上是有益的。