4 ms·
Summary: If superclass has method X with behaviour Y, do not override X to have behaviour Z in a subclass, because this violates the spirit of inheritance seman
by ambition 19y ago
Summary: If superclass has method X with behaviour Y, do not override X to have behaviour Z in a subclass, because this violates the spirit of inheritance semantics.
To be fair, his version is long, persuasive and well-written. And it has references.
- raganwald 19y agoAnd to be fair, your summary expresses the idea succinctly and directly. Bravo! May I quote you?
- ambition 19y agoOf course. I'm curious: Do you want to quote the summary, or the evaluation?
- Retric 19y agoGranted there is a loss of clarity when a sub class changes a parent classes behavior. But, there are still times where it can be useful. EX: You want to add a section to an online banking app that handles IRA accounts. It would be nice to reuse as much of the logging, balance transfer, and UI as possible but you don't want to alter well tested core classes. So you use inheritance to get something that works and is reasonably clean until you can refactor a cleaner solution.
- 0x44 19y agoIt is likely I'm misunderstanding, but why would you use inheritance instead of composition in your example?
- ericwaller 19y agoThis is why prototype based languages allow software to model our experiences much more accurately. Inheritance seems to rely on a platonic ideal, i.e. there is some universal sense of chair (parent class) from which office chair, rocking chair inherit. In reality that's not how we think about chairs. We can imagine the first chair was a tree stump which was incrementally refined, eventually into office chair, rocking chair, etc. When we wish to add heated chairs to our application we can simply add a heating element to our chair prototype. We can even have heated office chairs, heated rocking chairs which use the same heating element prototype. Much more flexible.
- raganwald 19y agoThis approach fits the WAS-A relationship much better than the IS-A relationship. A heated chair WAS A chair, but we fitted it with a heater. It might still have enough in common with the original chair that you can use it without any change on your part, but you never know, there was that accident when we decided an EjectorSeat WAS A Chair last year...
- ambition 19y agoI bet the alternatives that were considered were: class Parent { void a() {...} void b() {...} void c() {...} void d() {...} ... void zzz() {...} } class B { Parent m_a; void a() {m_a.a()} void b() {m_a.b()} void c() {m_a.c()} void d() { specialized logic } ... void zzz() {m_a.zzz()} } vs. class B : Parent { void d() { specialized logic } }
- Retric 19y agoYour first example works fine until void a() Parent.a() calls Parent.d() and you need the code to call B.d(). An interface also works but you end up cutting and pasting a lot of the same code between the two classes.
- 0x44 19y agoI see. His example is meaningful only in a language that does not provide a convention for handling missing methods in the composed class. For instance, in ruby, I can distill your example to: class Parent def a; ... end def b; ... end def c; ... end ... def zzzz; ... end end class B def initialize @parent = Parent.new end def b; special logic end def method_missing(methodname) @parent.send(methodname) end end
- raganwald 19y agoSpeaking as someone who has implemented bank accounts... Would you agree that all of our problems go away if we exposed an Account interface rather than an Account class? Then the impact of our choice about how to re-use the implementation is localized.
- Retric 19y agoThat brings up new issues as you now of 2 Account classes with identical functions in some areas and slightly changed functions in others so keeping fixes up to date can be an issue. IMO you can build an interface and an abstract class that implements that interface with 2 subclasses for checking Account vs IRA Account but I think the abstract classes works just fine on it's own.
- raganwald 19y agoIn that particular organization there is an interface hierarchy that is distinct from its implementation hierarchy. And in the implementation hierarchy, they avoid overriding existing methods in favour of using abstract classes. This might be what you are suggesting. The ultimate goal is to avoid using public inheritance to model WAS-A.
- scott_s 19y agoCorollary: factor out interfaces.