One code construct I encounter every now and then is what my colleague appropriately dubbed “the extranged child”, after I said that I do not have a proper name for it. It happens in OOP when a parent object creates child objects, and then later needs to interact with that child:
class Child { /* ... */ }
class Parent
{
Child Create() { /* ... */ }
void InteractWith(Child c) { /* ... */ }
}
This is all good, but as soon as inheritance enters the picture, it becomes more complicated:
abstract class BaseChild { /* ... */ }
abstract class BaseParent
{
public abstract BaseChild Create();
public abstract void InteractWith(BaseChild child);
}
class RealChild : BaseChild { }
class RealParent : BaseParent
{
public override BaseChild Create()
{
return new RealChild( /* ... */ );
}
public override void InteractWith(BaseChild child)
{
// We really want RealChild here...
var realChild = child as RealChild;
}
}
The interaction often needs details that only the child type associated with that specific parent type has, so that involves a smelly downcast at that point.
Just looking at the type system, this now violates the Liskov-Substitution-Principle also known as the L from SOLID.
One possible solution is adding a precondition for the InteractWith function. Something along the lines of “May only be called with own children”. That works, but cannot be checked by a compiler.
Another solution is to move the InteractWith function into the child, because at the point when it is created, it can know its real parent. That may not be the natural place for the function to go. Also, it requires the child to keep a reference to its parent, effectively making it a compound. But this approach can usually be done, as long as the relation of ‘valid’ child/parent types is one to one.
If you have a parent object that can create different kinds of children that it later needs to interact with, that approach is usually doomed as well. E.g. let the parent be a graphics API like OpenGL or DirectX, and the children be the resources created, like textures or buffers. For drawing, both are required later. At that point, really only the precondition approach works.
On the implementation side, the “ugly” casts remain. Stand-ins for the children can be used and associated with the data via dictionaries, hash-tables or any other lookup. This approach is often coupled with using (possibly strongly typed) IDs as the stand-ins. However, that really only replaces the downcast with a lookup, and it will also fail if the precondition is not satisfied.
Have you encountered this pattern before? Do you have a different name for it? Any thoughts on designing clean APIs that have such a parent-child relationship in a hierarchy? Let me know!
Using generic BaseParent<TChild> where TChild : BaseChild will work, if appropriate in this case.