What is the best way to initialize a child’s reference to its parent?

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 always show any.

  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 – reduces performance and adds 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 SE does not allow some subjective questions and clearly this is a subjective question. But, maybe it is a good subjective question.

5

I would stay away from any scenario which necessarily requires the child to know about the parent.

There are ways of passing messages from child to parent through events. This way the parent, upon add, simply has to register to an event that the child triggers, without the child having to know about the parent directly. Afterall, this is likely the intended usage of a child knowing about its parent, in order to be able to use the parent to some effect. Except you wouldn’t want the child performing the job of the parent, so really what you want to do is simply tell the parent that something has happened. Therefore, what you need to handle is an event on the child, of which the parent can take advantage.

This pattern also scales very well should this event become useful to other classes. Perhaps it is a bit of an overkill, but it also prevents you from shooting yourself in the foot later, since it becomes tempting to want to use parent in your child class which only couples the two classes even more. Refactoring of such classes afterwards is time-consuming and can easily create bugs in your program.

Hope that helps!

14

I think your option 3 might be the cleanest. You wrote.

Downside: The child is created without a parent reference.

I don’t see that as a downside. In fact, the design of your program can benefit from child objects which can be created without a parent first. For example, it can make testing childs in isolation a lot easier. If a user of your model forgets to add a child to its parent and calls a methods which expects the parent property initialized in your child class, then he gets a null ref exception – which is exactly what you want: an early crash for a wrong usage.

And if you think a child needs its parent attribute to be initialized in the constructor under all circumstances for technical reasons, use something like a “null parent object” as the default value (though this has the risk of masking errors).

2

There’s nothing to prevent high cohesion between two classes that are used commonly together (e.g. an Order and LineItem will commonly reference each other). However, in these cases, I tend to adhere to Domain Driven Design rules and model them as an Aggregate with the Parent being the Aggregate Root. This tells us that the AR is responsible for the lifetime of all objects in its aggregate.

So it would be most like your scenario four where the parent exposes a method to create its children accepting any necessary parameters to properly initialize the children and adding it to the collection.

1

I would suggest having “child-factory” objects which are passed to a parent method which creates a child (using the “child factory” object), attaches it, and returns a view. The child object itself will never be exposed outside the parent. This approach can work nicely for things like simulations. In an electronics simulation, one particular “child factory” object might represent the specifications for some kind of transistor; another might represent the specifications for a resistor; a circuit which needs two transistors and four resistors might be created with code like:

var q2N3904 = new TransistorSpec(TransistorType.NPN, 0.691, 40);
var idealResistor4K7 = new IdealResistorSpec(4700.0);
var idealResistor47K = new IdealResistorSpec(47000.0);

var Q1 = Circuit.AddComponent(q2N3904);
var Q2 = Circuit.AddComponent(q2N3904);
var R1 = Circuit.AddComponent(idealResistor4K7);
var R2 = Circuit.AddComponent(idealResistor4K7);
var R3 = Circuit.AddComponent(idealResistor47K);
var R4 = Circuit.AddComponent(idealResistor47K);

Note that the simulator need not retain any reference to the child-creator object, and AddComponent won’t return a reference to the object created and held by the simulator, but rather an object that represents a view. If the AddComponent method is generic, the view object could include component-specific functions but would not expose the members which the parent uses to manage the attachment.

Great listing. I do not know which method is the “best” but here is one to find the most expressive methods.

Start with the simplest possible parent and child class.
Write your code with those. Once you notice code duplication that can be named you put that into a method.

Maybe you get addChild(). Maybe you get something like addChildren(List<Child>) or addChildrenNamed(List<String>) or loadChildrenFrom(String) or newTwins(String, String) or Child.replicate(int).

If your problem is really about forcing a one-to-many relationship maybe you should

  • force it in the setters which may lead to confusion or throw-clauses
  • you remove the setters and create special copy or move methods – which is expressive and understandable

This is not an answer but I hope you find one while reading this.

I appreciate that having links from child to parent had it’s downsides as noted above.

However for many scenarios the workarounds with events and other “disconnected” mechanics also bring their own complexities and extra lines of code.

For example raising an event from Child to be received by it’s Parent will bind both together albeit in a loose coupling fashion.

Perhaps for many scenarios it is clear to all developers what a Child.Parent property means. For the majority of systems I worked on this has worked just fine. Over engineering can be be time consuming and…. Confusing!

Have a Parent.AttachChild() method that performs all the work needed to bind the Child to its parent. Everybody is clear on what this “means”

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật