【问题标题】:Is a parent-reference in a child justifiable if the child cannot exist without a parent?如果孩子没有父母就不能存在,那么孩子的父母参考是否合理?
【发布时间】:2015-06-24 22:14:51
【问题描述】:

例如,每个人可以有多个孩子,但每个人必须有两个父母。然后说(在 C# 中)是否合理

class Human {}

class Father : Human
{
    List<Child> Children {get; set;}
}

class Mother : Human
{
    List<Child> Children {get; set;}
}

class Child : Human
{
    Mother Mother {get; set;} 
    Father Father {get; set;}
}

(另一个例子是一本书的页面——一本书可以有多个页面,但一个页面必须属于一本书且仅属于一本书,假设我们不引入从书中撕页、添加页面等的概念)

我知道在 OOP 中,子对象中的父引用会破坏封装并增加耦合。但是,如果子对象没有父对象没有意义,那么为子对象添加父引用是否正确?

【问题讨论】:

  • 人类不必是母亲。因此,您可以创建一个类Human,从它派生一个Mother,从Human 派生一个ChildChild 是一个 Human,但它有一个 Mother,因此它应该有一个Mother 属性。 Mother 可以有多个Child,因此它应该有一个属性List&lt;Child&gt;
  • 谢谢,确实比较合适。更新了问题。
  • 实际上我想说的是,在语义的情况下,父 (Father/Mother) 应该持有Human 的列表,而不是Child。假设Human父母有Human孩子,这些孩子自己可以变成FatherMother。孩子的父母也是如此(应该是Human,因为父母是某些人的孩子)这消除了循环依赖,尽管您所做的事情在技术上没有任何问题,并且在某些情况下是描述对象的完全合法的方式。跨度>
  • 我不一定看到为母亲、父亲或孩子开设课程的价值。一个母亲有她自己的母亲,所以她应该被实例化为哪个类类型;母亲还是孩子?您可以简单地让 Human 类具有持有这些引用的母亲/父亲的属性,以及一个子列表。可能有名为 IsFatherIsMother 的属性根据子列表中的子数返回真/假...
  • @Idle_Mind 如果应用程序的任务是处理一组未成年人,FatherMother 将作为与他们有特定关系的人存在(尽管他们可能没有监护权,或者他们可能是其他监护人也必须考虑)。虽然通常不明智,但它可能存在于特定应用中。

标签: c# oop


【解决方案1】:

我知道在 OOP 中,子对象中的父引用会破坏封装……

不,它没有。如果来自外部的代码应该有理由关心存在这种关系,则允许来自外部的代码显示。代码不需要知道该关系是否由引用实现,实际上也不必如此。揭示这一点会破坏封装。

…并增加耦合度。

啊,确实如此。

所以你需要:

  1. 很高兴你不关心这个。
  2. 在明确的地方处理联轴器。

让你感到高兴的一种简单方法是,如果你的类是不可变的:当你收到 Human 时,它的 MotherFather 和/或 Children 属性是固定的并且可以读取 -只有这样,联轴器才会固定,不会伤害您。 (如果你有一个引用计数的垃圾收集,它可能会,但这是.NET,所以你没有)。这不是您可能不关心的唯一方法,但它是一种。

否则你需要处理这种耦合。一种方法是仅从外部设置MotherFather 属性。考虑:

public class Human
{
  private Human _mother;
  private Human _father;
  private HashSet<Human> _children;
  public IEnumerable<Human> Children
  {
    // If paranoid, do a foreach … yield here to stop code casting back to `HashSet`
    // Using ImmutableCollections and calling ToImmutableHashSet() is good too.
    get { return _children; } 
  }
  public Human Mother
  {
    get { return _mother; }
    set
    {
      if(_mother == value) // skip for no-op
        return;
      if(_mother != null)
        _mother._children.Remove(this);
      if(value != null)
        value._children.Add(this);
      _mother = value;
    }
  }
  public Human Father
  {
    get { return _father; }
    set
    {
      if(_father == value)
        return;
      if(_father != null)
        _father._children.Remove(this);
      if(value != null)
        value._children.Add(this);
      _father = value;
    }
  }
}

这种方法通过在FatherMother 属性中处理耦合来处理耦合,并且仅在这些属性中进行处理。其他方法适用于其他情况,例如根本不存储这些属性的值,而是在调用属性时有办法获取它们。

当然也可以让所有MotherFatherChildren 属性可写(以及通过访问Children 获得的集合也可写)` 并且仍然保持同步,这很难。是否比它必须的更难取决于应用程序。

他们非常依赖它,而不是打破封装。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-28
    • 1970-01-01
    • 2023-01-07
    • 2023-03-18
    • 2018-03-29
    相关资源
    最近更新 更多