One of the basic precepts of object oriented programming is that subtyping represents the "is-a" relationship. That is, the child is always a specific form of the parent. A common example is that a square is a specific form of rectangle, so it seems to make sense to make a Square
class inherit from a Rectangle
class. (read here why that doesn't actually make sense - thanks @KevinKrumwiede for pointing this out)
Occasionally, one encounters a situation where subtyping is a convenient way to write a different relationship.
The one I currently face is relatively simple. I'm controlling a simple mechanical system B, which contains subsystem A. Thus, in meatspace, we see a very literal "has-a" relationship: System B has a System A.
Writing the code as a close analogy to reality, I wound up with something like the following: (Shown in Python for brevity. Actual code is in C++.)
class A:
def m1(self): print "A.m1"
def m2(self): print "A.m2"
def m3(self, arg): print "A.m3(" + arg + ")"
class B:
def __init__(self): self.a = A()
def m1(self): a.m1()
def m2(self): a.m2()
def m3(self): print "B.m3"
def m4(self): a.m3("B.m4")
While class B
contributes useful functionality (in the real life version), a bunch of its methods are simple pass-throughs. Class B
just doesn't have anything useful to add to m1()
or m2()
- it just needs to expose them to the owners of B
objects.
If we throw out the precept that inheritance should represent an "is-a" relationship, we could simplify things:
class A:
def m1(self): print "A.m1"
def m2(self): print "A.m2"
def m3(self, arg): print "A.m3(" + arg + ")"
class B(A):
def __init__(self): pass
def m3(self): print "B.m3"
def m4(self): a.m3("B.m4")
Now all methods of A
are exposed as if they belong to B
. B
can (and does) override methods of A
as needed. The maintainer of B
doesn't need to update B
every time that A
adds a new method - passthroughs are free.
This feels wrong somehow, but I can't fully figure out why. Is this an abuse of the technicalities of the inheritance mechanism? Is it ever appropriate to represent a "has-a" relationship this way? B
is not a more specific form of A
, but we've subclassed it as if it was.
The only practical drawback I can think of is that all of A
's methods are now available for calling, and B
has little to no way to prevent this. In a language like Python, there's no concept of private methods, so that doesn't matter. In a language like C++ it might. Do other drawbacks exist?
Edit: To all who find this question in the future, be sure to read this answer as well as the accepted answer. Both list noteworthy drawbacks to using inheritance to accomplish composition.