views:

769

answers:

8

I'm fairly new to the OO design process, so please bear with me....

I have two entities that I need to model as classes, call them Parent and Child (it's close enough to the actual problem domain). One Parent will have one or more Children -- I have not interest, in this application, in childless Parents.

Where my brain is going out to lunch is on the fact that I need to be able to find either from the other. In my database I can implement this with a normal foreign key relationship, and the set-based nature of SQL makes it easy to find all Children for a given Parent, or the Parent for a given Child. But as objects...?

I think that the Parent should carry a collection (list, whatever) of Children. I also think that each Child should carry a reference to its Parent. The circular nature of the references, however, is making my head hurt.

Am I:

  • On the right track?
  • Completely off base? If so, what should I do differently?

This will almost certainly be implemented in VB.NET, but I'm a ways from cutting code yet.

Edit after 8 answers:

Thanks all. It was hard to pick just one answer to accept.

To clarify a couple of things that were questioned in the answers:

  • Parent and Child are very different entities--there's not inheritance relationship at all. I chose the names that I did because they're really very close to the real-world problem domain, and now see that it's a source of confusion from an OO perspective.
  • The hierarchy is only one level deep--Children will never have Children within the application.

Thanks again.

A: 

Sounds to me like you're on the path to a bad design. Your architecture should never have circular references.

You should probably re-examine why your children need a reference back to the parent and vice versa. I would lean toward the parent having a collection of children. You can then add functionality to the parent to check to see if a child object is a child of the instance.

A better explination of the goal might be a little more helpful as well...

EDIT

I read up a little more (and listened to comments)...and it turns out I'm quite in the wrong. Circular references do in fact have their place as long as you're careful with them and don't let them get out of hand.

Justin Niessner
"Your architecture should never have circular references."Why not? Legitimately curious.
Chris McCall
In theory, a circular reference like that could lead to code that loops infinitely. Say you have code that loads your parent, then loads the children, which loads the parent again, which loads the child again, and on...and on...
Justin Niessner
ORM tools often generate circular references for associations by default. Doesn't necessarily indicate a design flaw.
womp
@Justin since when did "it could be used improperly" become a good reason not to implement a design?
Spencer Ruport
Good point. Read a couple of other articles after I shot off my answer. I missed a couple of good reasons to have circular references....time to refrain from SO when overly tired.
Justin Niessner
It looks to me like the folks downmodding you are misunderstanding both your post and the question.
Randolpho
+1 for the edit. I can see why it was a legitimate concern at first, though....
RolandTumble
A: 

If I understand that objects of P contain an array of objects P->c[] representing children. And any node P with no children is a leaf ... with each P containing P->P' (the parent).

The solution you specify, with Parents containing references to children and vice versa eliminates the need to traverse the tree to obtain ancestry of a given child and children of a node. This is really just a tree that can you perform all kinds of links on and algorithms to traverse and enumerate it. Which is fine!

I suggest reading the trees chapter in The Art of Computer Programming for an excellent and in-depth look at tree structures and efficient ways to enumerate parentage and children.

Aiden Bell
A: 

Are you talking about a class hierarchy, where the parent class knows about its child classes?

You should avoid this at all costs.

By default, a child class knows all about a parent class, because it is an instance of the parent class. But to have a parent class know about its child classes requires that the child class also know all about every other child class. This creates a dependency between one child and every other child of that class. This is an unmaintainable scenario that will cause problems in the future -- if you can even get it to compile or run, which in many languages will not be the case.

That said, it sounds to me like you're not trying to do a class hierarchy, but a collection hierarchy, i.e. a tree. In that case, yes, you're on the right track; it's a common paradigm. The parent node has a collection of child nodes, and the child node has a reference to the parent node.

The thing is? They're all the same class! Here's a very simple example in C#:

public class Node
{
  public readonly Node Parent; // null Parent indicates root node
  public readonly List<Node> Children = new List<Node>();
  public Node(Node parent)
  {
     Parent = parent;
  }
  public Node()
  {
     parent = null;
  }
  public void AddChild(Node node)
  {
     Children.Add(node);
  }
}

I have a feeling this is what you're really after. Using this paradigm, you would then sub-class Node for whatever nefarious purposes you might have.

Randolpho
It sounded to me like a containment rather than inheritance relationship.
Robert
@Robert: I agree, but the questioner wasn't very clear, which is why I asked for clarification then edited.
Randolpho
The classes will be different from each other. Children are (very) not the same thing as Parents. But I can see where I wasn't all that clear at first.
RolandTumble
@RolandTumble: Which is fine. The paradigm works for heterogeneous situations just as well. The problem I worry about, and which I recommend you avoid at all costs, is the case where Child derives from Parent rather than being a Child node of Parent.
Randolpho
+6  A: 

Sounds like you're on the right track to me. As per your domain model, parents have children and children have parents. You may need to reference each from the other.

There is nothing wrong with circular references, you just have to be careful about what you do with them. Where you'll run into trouble is managing your entities on the server side in an automated fashion when you load them from the database. For example, you fetch a Child object from the database with a query. Do you include the parent information? Do you include the parent's children?

ORM tools like Lightspeed or Microsoft's Entity Framework generally deal with this using "lazy loading" directives. They'll fetch what you need at first (so, when you fetch a Child, it just gets the Child properties and the parent's ID). If later, you dereference the Parent, it goes and fetches the Parent properties and instantiates the Parent object. If later still, you access it's Children collection, it then goes and fetches the relevant child information and creates Child objects for that collection. Until you need them though, it doesn't populate it.

womp
+1  A: 

I think it's reasonable to want to be able to traverse the object graph in this way. It's hard to know if you have a justifiable reason for it from your post, but I don't think the references in and of themselves prove a bad design.

Robert
+1  A: 

I believe you're on the right track. Why is the circular nature of the references making your head hurt? What is the fundamental issue you're having with a Parent having references to its children, and a Child having a reference to its parent?

McWafflestix
I guess it's just the training of all those years of normalized table structures....
RolandTumble
Ah, yes, I understand. Keep in mind that in the SQL world, there's an additional data source; the actual database itself which contains the tables which can be queried; there's a "forward reference" at the database level to the parent which is used there; in your class structure, there is no such queryable reference by which to look up the parents. In object design, normalization can be a fault, because of the inability to query on the data; references must be explicit.
McWafflestix
@McWafflestix: what a great concise explanation - thanks!
Skilldrick
+7  A: 

The circular references are fine and absolutely standard when creating a tree structure. HTML's Document Object Model (DOM), for example, has the parent and child properties on every node in a DOM tree:

interface Node {
    // ...
    readonly attribute Node     parentNode;
    readonly attribute NodeList childNodes;
    // ...
}
John Kugelman
+1 ... Good example if circular definition not mattering in *some* languages (... try this in C :P ...) due to automatic allocation
Aiden Bell
This is not a circular reference.
Randolpho
@Randolpho - no it is circular definition
Aiden Bell
Yeah that term is being used incorrectly by a number of people here and adding to the confusion.
Spencer Ruport
Maybe an edit will happen :)
Aiden Bell
@Aiden Bell: Perhaps we should just call it a self reference?
Randolpho
@Randolpho - Depends on semantics ... when the compiler reads it ... it is a circular definition ... which applies only to resolving methods and such and allocating space when requirements are unknown. For an instance of class x with member x.y->x that is self reference. If x.y.z->x then we have circular reference if the algorithm would traverse the object heirarchy as such.
Aiden Bell
A: 

If the children must have a parent I usually just require a parent type instance in the child constructor.

Spencer Ruport