Addressing the fragile base class problem
I’ve been thinking about the fragile base class problem lately. (Yes, I know it’s almost Christmas. My mind works mysteriously.) I started thinking by analogy to APIs, which the interface a superclass gives a subclass in fact is, even if it’s not called that. So, the superclass’s API changes, breaking the subclass, just like a regular API’s change can break a client. How do we deal with this with regular APIs? If we are to make a compatibility-breaking change (which introducing any member into a superclass potentially is), we version the API so that a client requesting version 1 semantics gets them while only clients written against the newer semantics will request version 2. We could do the same kind of thing with class inheritance if we mark everything with revision numbers, which we reference when inheriting.
class base@2 {
void start@1();
void stop@1();
void idle@2();
};
class child@1 extends base@1 {
void idle@1();
void park@1();
};
Here’s our classic case of a fragile base class. child
subclassed base
and defined the new method idle()
, then later base
was extended with its own method idle()
. Normally, this would cause a problem — the new stop()
implementation might call idle()
perhaps, and child
’s idle()
won’t be written with overriding a then-nonexistent base::idle()
in mind. But with these revision markings, we say that child
only overrides methods marked as being in revision 1 of base
. So, when stop()
calls idle()
, it gets base::idle
, not child::idle
, and when park()
calls idle()
, the call resolution goes the other way.
The problem I see with this though, is that when going to an indirect superclass, it can be unclear which revision that should be.
class grandparent@3 {
void method@2();
};
class parent@2 extends grandparent@2;
class child@1 extends parent@1 {
void method@1();
};
Uh-oh. Should child
’s method()
override grandparent
’s? If parent@1
extended grandparent@2
, then yes. But if it extended grandparent@1
, then no. So do we need to list the parent class revisions of every revision of the child class? I’d hope there’d be a better way. Perhaps we’d be relying on an IDE to handle the revision numbers for us, keeping them updated is just a dumb task, so in that case the IDE could maintain the manifest of parent revisions too.