This post originated from an RSS feed registered with Python Buzz
by Patrick Lioi.
Original Post: Proper OOP
Feed Title: Patrick Lioi on Python
Feed URL: http://patrick.lioi.net/syndicate/Python/RSS2.xml
Feed Description: Entries from the Python category of my personal site.
There are strong arguments favoring inclusion over inheritance
in object oriented programming languages.
Inheritance breaks encapsulation. You often have to
know the internals of an object to subclass it properly. Even
when done with relative care, it is not very difficult to produce
spaghetti code when subclassing. Side effects abound. Having to keep track of
implementation details at all levels of a hierarchy can be
overly complicated, which can be disastrous if changes need
to be made in the middle of the hierarchy.
So, we should favor inclusion over inheritance as much as possible.
This way, you keep your abstractions in order by keeping details
hidden behind your interfaces. Your interfaces are easier to memorize than the code that they hide.
As long as the maintainer of an object keeps his interface
intact, your including-code works just like you want it to. (If the maintainer
breaks his interface, everybody is screwed regardless of inheritance-vs-inclusion.)
Despite this argument, we can't just go whole hog with inclusion. That would lead to
reinventing the wheel far too often. So, what's the best way to compromise?
I'd say that python encourages doing things the Right Way, without
necessarily forcing it upon you. By allowing multiple classes to appear
in the same module -- that is, in a single source file -- the programmer
is encouraged to use "small scale inheritance", within the scope of
the particular module he wishes to expose to the rest of the world.
The person(s) responsible for a single source file are the ones fit to
know about implementation details, and are fit to deal with avoiding side
effects. The outside world can then use those objects via inclusion without
worry. Java fails here in that placing multiple public classes in the same
package means placing them in separate source files. Python's organization
has a gentle psychological effect on the programmer, encouraging this small scale
inheritance within a domain that he has already determined to be a cohesive unit.
His goal is to make that unit, and all the classes that make it up are apt to be
"aware" of each others' guts.
But there are still times when it's a good idea to subclass across modules.
UserDict is a __builtin__ class that lets python programmers extend the built-in
dictionary class. If your subclasses don't make assumptions about the
internals of parent classes, inheritance-across-modules lets you do some pretty
elegant things with little effort. For example, if there's a SAX parsing class
that lets you define event-handling methods, it can be more elegant to do so with
inheritance than the alternatives.
So, there seem to be special cases in which inheritance-across-modules is not only
allowed, but encouraged. What these special cases have in common is that the obvious
intended use of subclassing is inherent to the API of the parent class. The parent
class interface is clearly specified for the purpose of subclassing, the parent
class interface is small, and the methods meant to be overridden have a clear
purpose in the act of subclassing. Overriding SAX event handlers makes sense because
that is what they are there for in the API.
Subclassing across modules just
doesn't work for more arbitrary situations, where the goal of the particular
subclassing falls outside the scope of the obvious intended use inherent in the
parent API.
To sum up:
Actively subclass on a small scale, within modules, to benefit from code reuse.
Favor inclusion across modules to avoid spaghetti code.
Subclass across modules only when the parent class was designed to be subclassed across modules.
Design such parent classes to have clear and small API's, where the intended purpose of subclassing is inherent in the API.