0

I'm developing an object model that has lots of different parent/child classes. Each child object has a reference to its parent object. I can think of (and have tried) several ways to initialize the parent reference, but I find significant drawbacks to each approach. Given the approaches described below which is best ... or what is even better.

I'm not going to make sure the code below compiles so please try to see my intention if the code is not syntactically correct.

Note that some of my child class constructors do take parameters (other than parent) even though I don't show any here.

1) Caller is responsible for setting parent and adding to the same parent.

class Child{
  public Child(Parent parent){Parent=parent;}
  public Parent Parent {get; private set;}
}
class Parent{
  // singleton child
  public Child Child {get;set;}
  //children
  private List<Child> _children=new List<Child>();
  public List<Child> Children { get {return _children;}}
}

Downside: setting parent is a two-step process for the consumer.

var child = new Child(parent);
parent.Children.Add(child);

Downside: error prone. Caller can add child to a different parent than was used to initialize the child.

var child = new Child(parent1);
parent2.Children.Add(child);

2) Parent verifies that caller adds child to parent for which was initialized.

class Child{
  public Child(Parent parent){Parent=parent;}
  public Parent Parent {get; private set;}
}
class Parent{
  // singleton child
  private Child _child;
  public Child Child {get{return _child;}
    set{
      if (value.Parent != this) throw new Exception();
      _child=value;}}
  //children
  private List<Child> _children=new List<Child>();
  public ReadOnlyCollection<Child> Children { get {return _children;}}
  public void AddChild(Child child){
    if (child.Parent != this) throw new Exception();
    _children.Add(child);
}

Downside: Caller still has a two-step process for setting parent.

Downside: runtime checking -- performance and need to add code to every add/setter.

3) The parent sets the child's parent reference (to itself) when the child is added/assigned to a parent. Parent setter is internal.

class Child{
  public Parent Parent {get; internal set;}
}
class Parent{
  // singleton child
  private Child _child;
  public Child Child {get{return _child;}
    set{
      value.Parent=this;
      _child = value;
  }}
  //children
  private List<Child> _children=new List<Child>();
  public ReadOnlyCollection<Child> Children { get {return _children;}}
  public void AddChild(Child child){
    child.Parent=this;
    _children.Add(child);
  }
}

Downside: The child is created without a parent reference. Sometimes initialization/validation requires the parent which means some initialization/validation must be performed in the child’s parent setter. The code can get complicated. It would be so much easier to implement the child if it always had its parent reference.

4) Parent exposes factory add methods so that a child always has a parent reference. Child ctor is internal. Parent setter is private.

class Child{
  internal Child(Parent parent, init-params){Parent=parent;}
  public Parent Parent {get; private set;}
}
class Parent{
  // singleton child
  public Child Child {get; private set;}
  public void CreateChild(init-params) {
      var child = new Child(this, init-params);
      Child = value;
  }
  //children
  private List<Child> _children=new List<Child>();
  public ReadOnlyCollection<Child> Children { get {return _children;}}
  public Child AddChild(init-params){
    var child = new Child(this, init-params);
    _children.Add(child);
    return child;
  }
}

Downside: Can’t use initialization syntax such as:

new Child(){prop=value}

Instead have to do:

var c = parent.AddChild(); 
c.prop=value;

Downside: Have to duplicate the parameters of the child constructor in the add-factory methods.

Downside: Can’t use a property setter for a singleton child. It seems lame that I need a method to set the value but provide read access via a property getter. It’s lopsided.

5) Child adds itself to the parent referenced in its constructor. Child ctor is public. No public add access from parent.

//singleton
class Child{
  public Child(ParentWithChild parent){
    Parent=parent;
    Parent.Child=this;
  }
  public ParentWithChild Parent {get; private set;}
}
class ParentWithChild{
  public Child Child {get;internal set;}
}

//children
class Child{
  public Child(ParentWithChildren parent){
    Parent=parent;
    Parent._children.Add(this);
  }
  public ParentWithChildren Parent {get; private set;}
}
class ParentWithChildren{
  internal List<Child> _children=new List<Child>();
  public ReadOnlyCollection<Child> Children { get {return _children;}}
}

Downside: calling syntax is not great. Normally one calls an add method on the parent instead of just creating an object like this:

var parent = new ParentWithChildren();
new Child(parent); //adds child to parent
new Child(parent);
new Child(parent);

And sets a property rather than just creating an object like this:

var parent = new ParentWithChild();
new Child(parent); // sets parent.Child

...

I just learned that SO does not allow some subjective questions and clearly this is a subjective question. But, maybe it a good subjective question.

| |

Browse other questions tagged or ask your own question.