Reputation: 6217
Is it bad practice, and if so, why, to return instances of child classes as output of methods of an instance of the parent class?
For example, can the following --
class Parent(object):
def __init__(self, attr):
self.attr = attr
def child(self, typ):
if typ == 'a':
return ChildA(self.attr)
else:
return ChildB(self.attr)
class ChildA(Parent):
pass
class ChildB(Parent):
pass
be a good design?
Upvotes: 6
Views: 4749
Reputation: 9614
I would appreciate if someone more knowledgeable answers this question, but as this has yet to happen, I'll throw in my two cents' worth.
I think that it's indeed not good practice. Parent classes shouldn't directly reference child classes for a number of reasons:
First and foremost, it impedes future extensions of the module. Whenever a new child class is introduced, it would probably require updating the parent class as well in order to take this new child class into account in its implementation. This breaks the open-closed principle of the SOLID principles of object-oriented programming.
Another drawback, at least in Python, is that it forces all child classes to be defined in the same file, in order to avoid circular imports.
Furthermore, since the specified method is inherited by the child classes, then unless it's overridden, it triggers some kind of familiarity between "brother" classes, which might not be desired.
Although the technical issues may be circumvented with some coding effort, this approach unnecessarily places another burden on the programmer.
I'm sure that there are certain situations where it would seem useful for a parent class to be made aware of its child classes, however I think that in general, such situations infer an error in design and an alternative solution that doesn't involve this kind of introspection should be sought after.
Note that this doesn't mean parent objects shouldn't utilize child objects. Child objects, like all other objects, may be legitimately received as arguments or returned by their parent's methods. The parent's methods may even treat them as ordinary parent objects since a child object is also a parent object and that's one of the main pillars of object-oriented programming.
Regarding your particular example, consider the factory design pattern. For example:
class Parent(object):
def __init__(self, attr):
self.attr = attr
class ChildA(Parent):
pass
class ChildB(Parent):
pass
class Factory(object):
def __init__(self, parent):
self.parent = parent
def create(self, <some_external_data>):
if <an algorithm that determines the desired type based
on some_external_data and the state of the parent>:
return ChildA(parent.attr)
else:
return ChildB(parent.attr)
Upvotes: 5