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")
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
m2() - it just needs to expose them to the owners of
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 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?