Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cross-reference objects in classes

Tags:

c#

oop

I have a Person class and two inherited classes called Parent and Child. A Parent can have n Child(s) and a Child can have n Parent(s).

What is the best way in OOD to create a reference between a Parent and a Child.

Should I create a List in each class referencing the connected Parent/Child or is there a better way?

like image 487
Zooking Avatar asked Mar 06 '10 16:03

Zooking


4 Answers

Great question. Pure many-to-many relationships are actually quite rare, and it usually helps to introduce an intermediate object to model the relationship itself. This will prove invaluable if (when!) use cases emerge which require the capture of properties regarding the relationship (e.g. whether the child/parent relationship is natural, surrogate, adoptive, etc).

So, in addition to the Person, Parent and Child entities which you've already identified, let's introduce an object called ParentChildRelationship. An instance of ParentChildRelationship will have a reference to exactly one Parent and One Child, and both the Parent and Child classes will hold a collection of these entities.

It's a good idea to then identify the use cases you have for working with these entities, and add appropriate helper methods to maintain the inter-object references. In the example below I've just chosen to add a public AddChild method to the parent.

alt text

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
}
like image 92
Ian Nelson Avatar answered Oct 04 '22 09:10

Ian Nelson


public class Person
{
    Person Parent { get;set; }
    IList<Person> Children { get;set; }
}

Parent can be null when you do not know the parent. Children can be null or empty when you have no children. Since each child is a Person, it can have a Parent or its own children.

This design, by itself, is fine until you provide more detailed use case scenarios about how it will be used or persisted.

like image 33
Thomas Avatar answered Oct 04 '22 08:10

Thomas


If you can limit the direction of the association to go only one way, you will save yourself a lot of trouble (but this is not always possible).

One-way relationship:

public class Parent : Person
{
    public IEnumerable<Person> Children { get; }
}

If you want to have the association going the other direction as well, you can do so too:

public class Child : Person
{
    public Parent Parent { get; }
}

However, now you have a circular reference that you need to maintain, and while it's possible, it's not particularly productive.

You can often keep the association as a one-way relationship by letting the children raise events instead of explicitly referencing their parent.

like image 41
Mark Seemann Avatar answered Oct 04 '22 10:10

Mark Seemann


As JohnIdol pointed out, a child someway down the line might become a parent. In other words DON'T make Parent and Child sub-classes of Person.

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... */
}
like image 36
Robert Davis Avatar answered Oct 04 '22 09:10

Robert Davis