This question is about OOP in any statically typed language. Suppose I have two classes whose instances maintain pointers/references to each other. In my case, one class is a container, and the other a wrapper around a contained object which maintains a pointer to the container in which it lives.
class Container {
Element[] elements;
}
class Element {
// ... data...
Container holds_me;
}
The constructor of Container creates an Element object to wrap each contained object, and sets their holds_me pointers to itself.
Now I want to inherit from these classes. I want a DerivedContainer class, which inherits from Container and contains DerivedElement objects, where DerivedElement inherits from Element and refers to the containing DerivedContainer object. What is the right way to do this (or is it the wrong thing to do)?
The most straightforward thing is for the constructor of DerivedContainer to create DerivedElements and store them in elements and set their holds_me pointer to itself. Then all the methods of Container and Element will work, but any new methods defined in DerivedContainer and DerivedElement will have to downcast the objects held in elements and holds_me in order to invoke any new methods on them that weren’t defined in the base classes. This doesn’t seem pretty; so I wonder, is there a better solution?
Yep, this is the right way to do it, without any more information, IMHO. It makes sense if you think that all of the methods in Element can apply to every Element, but the only classes that should know anything about the Derived set of functionality are (ideally only) DerivedElement and (if necessary) DerivedContainer. In other words, to anyone else, Elements and Containers are only Elements and Containers.
You can sometimes do a little better with templates(C++) or generics(Java), since the thought behind these features is that a
Container<Element>knows that it holds Elements and aContainer<DerivedElement>knows that it holds DerivedElements, but if you have a heterogeneous Container, you really have to have each subclass handle the derived functionality by trying to downcast.