Raganwald on Inheritance
weblog.raganwald.com
weblog.raganwald.com
To be fair, his version is long, persuasive and well-written. And it has references.
May I quote you?
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.
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.
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...
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 }
}An interface also works but you end up cutting and pasting a lot of the same code between the two classes.
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
endWould 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.
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.
The ultimate goal is to avoid using public inheritance to model WAS-A.
BTW, Ruby and Java both solve this problem. In Java, use the @override annotation. If you are not overriding a method, you get a compiler error.
Ruby "solves" the problem by only having one method per name. But you can mix in some magic and get pattern matching if you want.