I remember reading about polymorphism for the first time. I was in high school, and boy it sure looked cool! Too bad I didn’t realize that the myth of polymorphism was a bunch of poppy cock.
You see, polymorphism never seems to be presented in its real state. Instead, we get this goofy, toy-app type presentation. Does Shape -> Rectangle -> Square ring a bell?
The fallacy of geometrical shapes being polymorphic
When discussing things in light of geometry, the reason we value this relationship is because we are looking at things like angles, parallelism, vertices, and intersections. Hence, squares carry all the attributes of rectangles. They simply have the same width and height.
If we grabbed a square, set its width, then set its height, the assumptions of what would happen is unclear. Should a square morph into a rectangle? Or should setting the height induce the side effect of also updating the width? Either way is bad form. Hence, its best to break apart this faulty geometric relationship and realize that squares are NOT rectangles.
The fallacy of inheriting behavior
The problem is, no programming language has adequately been invented to gather ALL the semantic nuances of code. As more and more classes extend a given class, they all either realize EXACTLY what the abstract parent class does and agree with it, or they discover some new wrinkle not quite handled. The API may be supported, but some underlying assumption is buried that requires an update.
Some of the avenues to remedy this involves opening up the API a bit more. Perhaps a private utility method is needed by a new extender. But opening it up introduces more maintenance down the road. Or more opportunities for others to abuse things that used to be kept tightly controlled.
History has proven that composition beats inheritance for sustainability. Raise your hand if maintenance, not new development, doesn’t encompass much of your work.
The alternative are more sophisticated languages where you can capture more of the concepts. Yet these languages come across as too complex to many, arguably because CAPTURING all semantics is inherently challenging. And more often than not, we don’t KNOW all these semantics on the first round.
The myth of polymorphism vs. the reality
So when it comes to abstract and refactor, think about extracting interfaces when possible and delegating solutions.
Strangely enough, despite my consternation with static variables, I’ve come to appreciate static methods in the right scenario. Factories that support interfaces can be quite handy dandy. But a well modeled, inheritance hierarchy is to hard to accomplish. If possible, try to avoid multiple layers, and see if it’s not possible to extract share code into various utility classes.
And when the entire hierarchy is nicely contained INSIDE your framework, and not as an extension point to consumers, the fallacies of polymorphism can be contained. But watch out! It takes much effort to put this together. Never think it will be easy.
Of course, I could be entirely wrong. If you have some keen examples, please feel free to share!
Happy coding.
0 Comments