Click to See Complete Forum and Search --> : OO design question/problem: abstract Child/Parent!


Jure
October 1st, 2002, 06:13 AM
OK - here's the problem/question.

We have two pure abstract interface classes - no implementation (remember that). One is a Child and one is a Parent. Parent objects can have multiple Child objects added to them and Child objects know their Parent object. Parent derives from Child so that it's possible to add a Parent object to a Parent object - but Child objects on the other hand can't have children. OK, let's see some example (prototype) C++ classes:

// X class name prefix means: abstract, not known (regarding implementation) (btw) ...

class XChild
{
public:
// "knows it's parent"
virtual XParent* getParent() = 0
//... ?
//... ?
};

// Parent is a Child too
class XParent : public XChild
{
public:
virtual void addChild(XChild* child_p) = 0;
virtual void removeChild(XChild* child_p) = 0;
//... ?
//... ?
};

If we say "parent.addChild(X)" the child must (assuming here he has no parent yet) update it's "knowledge" about the parent, and the parent should attach the child to it self. OK, the child could have setParent method - but if you would say "child.setParent(X)" than the parent should also remove a reference to the child.

The question/problem is: How would the abstract interface look like if we wanted NOT to break those abstractions at any cost! That means that there should/could be no assumptions regarding implementation the XParent/XChild.

Also, the potential misuse of methods should be minimized - if possible.

OK, please post your opinions/ideas/...?

Frank_1974
October 1st, 2002, 06:37 AM
Let me understand . I'm getting into it.
I have analized the problem and I can't understand
what you mean for "your" abstract interface .
You have already two interfaces and you derive
a parent from a child just to separate the two things.
So you can use a child or a parent.
The Way a parent should create a child is passing also
a reference to himself . So the child knows who's it's
parent and ofcourse it's parent knows the child because
he has created his child ( and appends it in private list)
Also if this list was protected and visible to whom generalizes
from the parent class, in this way ,childs could keep track of
their brothers
So Why would you change your interafce ?
Let talk about this.

Frank

Jure
October 1st, 2002, 08:10 AM
First. I want to discuss what would be the best interfaces for Child/Parent if we can not use ANY implementation. I don't say it would be a bad idea, for example, to use multiple implementation inheritance that's allowed in C++. I just want to find out what's the best thing we can do if we (let's say) don't or can't use multiple imp. inheritance - and instead use multiple interface inheritance and define only A WAY how should things work. In short - what interfaces to define, if we limit ourselves to only that :).

That means that Child/Parent objects could be implemented totally independent of each other and still compatible through standard abstract interfaces. They would be a complete "black box" when implemented.

OK. Parent inherits from a Child so that a parent CAN have children and a child CAN'T. For example an UI button (Child) can't have child UI widgets but a window can (Parent). A window can also have child windows (because a Parent is derived from a Child).

The Way a parent should create a child is passing also
a reference to himself . So the child knows who's it's
parent and ofcourse it's parent knows the child because
he has created his child ( and appends it in private list).


If I understand you right - you propose that a Parent object should create it's own Child objects and at creation time pass a reference of it self to them?

If the underlying implementation is unknown, how would you pass the parent's reference to a Child object?

Well, Child objects should be addable and removable (not destroyable) at any time, multiple times. So Parent objects wouldn't/shouldn't own Child pointers. It should be possible to remove a child from parent and then add that child to some other parent while preserving valid object states. (the question is how should the interfaces look like ...)

If we'd use a protected child list we'd use implementation that would prevent any other derived classes to be able to provide their own custom implementation.

(Even I'am confused now <:) )

Graham
October 1st, 2002, 08:34 AM
This is a bit confusing and it looks dubious. Bear with me here: I want to restate your classes, but with different names. The names you have used (parent and Child) are "loaded" and cloud the issue of what's going on, so let's restate it with neutral names and try to work out from the interfaces only what is happening:

class A
{
public:
// "knows its parent"
virtual B* getB() = 0
//... ?
//... ?
};

// B is a A too
class B: public A
{
public:
virtual void addA(A* child_p) = 0;
virtual void removeA(A* child_p) = 0;
//... ?
//... ?
};

OK. First thing to notice is that A knows about B, a derived class. This could be an indicator of future problems.

Apart from that, we have a class, B, that is a container for another class, A. A knows about the container that it is contained in. Additionally, another B can contain another instance of B, since B is derived from A (the stored class). The inheritance here is dubious since it seems hard to justify the statement "B IS-A A", especially when you restate it as "Parent IS-A Child". If Parent and Child have anything like their normal meanings, you'd have an almost impossible job of justifying that relationship.

The Gang of Four book describes the "Composite" pattern. This pattern describes a structure that allows you to store arbitrary lists of different classes, including further lists. The overall class hierarchy structure is:

class Component
{
public:
virtual void operator()() = 0;
};

// The "Leaf" class represents any number of simple concrete
// classes....
class Leaf : public Component
{
public:
void operator()() {/* do whatever */}
};

// This is the one that allows the tree structure to be built
class Composite : public Component
{
public:
void operator()() {/* call operator() on all children */}
void Add(const Component*);
void Remove(const Component*);
Component* GetChild(int);
private:
std::list<Component*> children_;
};

So far, this doesn't answer the question about hiding the "set/get parent" issue, but it does rationalise the class structure and remove the circular reference. Note also that we no longer have to justify "Parent IS-A Child", we have instead two statements: "Leaf IS-A Component" and "Composite IS-A Component", both of which seem much more reasonable.

I've often found that one way to solve a tricky problem is to restate the question until it goes away :) . What I mean by that is: if I find that my requirements are hard to code (or result in ugly code), I will have another look and ask "what do I really want to do?" Often, I will find that I don't actually need to do the ugly thing, but can achieve the real goal much more simply. So the question is: why do you think you need to have a pointer to the parent? Note that I'm not saying that you necessarily don't need that pointer, just that if we know why, then maybe we can come up with an alternative that's not as horrible.

If you haven't already got it, get hold of "Design Patterns" by Gamma, et al. and read up on the Composite pattern. You may get some good mileage out of it (and the book is well worth getting, anyway).

Jure
October 1st, 2002, 10:19 AM
OK. B certainly isn't A if we compare for equality. But if we compare for capabilities or properties - B is A - or better: B has all the properties of A (in an aspect of A's properties: B is the same as A). I think it's possible to justify "parent is a child" - child can have a parent, parent can have a parent, child can't have a child (I mean they shouldn't :)) - parent is in THAT aspect equal to a child. OK. I agree that Parent/Child names are "loaded" with some real world stuff - better names would perhaps be Container and Containable. Container is Containable but Containable is not a Container:


class Containable
{
...
};

class Container : public Containable
{
...
};


With my post I wanted to find out what would be the best way to define an abstract interface for Child/Parent (Container/Containable) with the relationships I described to find out if there is a non-ugly way ...


So the question is: why do you think you need to have a pointer to the parent?


You really made me think with that :confused:. Maybe I really don't need it. Your post was really useful (btw, I'm constantly searching the web for those patterns - I got to get the book!). I think I could live without having the child objects to know their parents and use maybe use the composite pattern (but I think Composite is essentially a Child/Parent "pattern" - it solves the problem of Child/Parent by removing the "knowledge" of Parent from a Child - if the same thing was done on Child/Pattern, there would be no problem also ...)

The question still remains unanswered. Removing the knowledge of a child about his parent is a practical solution only - (which, is quite qood actually :) )

Let's concretize a bit.

We have a Button and a Window. Both, a Button and a Window are Component-s. Buttons can't have child Components, but a Window can. Also, a Window can contain a child Window. Every Component knows it's parent Component because it ... just want's to have that information :) (let's say for passing messages to it's parent). All Component-s are created via an abstract factory mechanism that returns a PURE interface pointer to the created object (btw, that's way we can not depend on implementation - we want to eliminate that dependency :D ).

How should this be represented?

Graham
October 1st, 2002, 10:40 AM
There are a couple of patterns that can avoid children knowing about their parents (or Containeds knowing about their Containers - good choice of names, that - it makes more sense).

For example: Chain of Responsibility uses an abstract "Handler" class to pass messages between other classes. Each class that can take a Handler implements a concrete class that handles those messages applicable to it. If it doesn't handle a message, the Handler passes the message along to the next in the chain.

class Handler
{
public:
Handler(Handler* s) : successor_(s) {}
void HandleMessage(/*message*/)
{
if (!DoHandle(/*message*/))
successor_->HandleMessage(/*message*/);
}
private:
virtual void DoHandle(/*message*/) = 0;
Handler* successor_;
};

Now, your Container and Contained classes define handlers. When you add a Contained to the Container, the Container passes a pointer to its handler to the Contained. Contained can now send messages back to the Container - the only dependency is that both classes must agree on the format of the messages.

Note also the way there is no limit on the length of the chain that the message can be passed back through.

There's also the Command pattern, but I don't think that fits your problem quite as well as Chain of responsibility.

Jure
October 1st, 2002, 11:33 AM
Thanks for your replies Graham! Very useful!

I'm working an an user interface, that doesn't do such chained event handling - but it's good to get some useful answers on how to solve such problems. Well, in my UI you always have to register appropriate event handlers with a component that performs some actions. So technically you don't need to pass anything up the hierarchy as far as messages are concerned because events are delivered by interest. But there are some other potential issues ... hm - but I'll first try to use your hints :D.

Thanks!