2

We can do something like this:

// in base class
protected virtual void Init(){
   //do stuff that all objects need to do
}

//in derived class
protected override void Init(){
   base.Init(); // if you forget this but it compiles all hell breaks loose
   //do derived stuff related to this object
}

I spent a good 15 minutes with a logic error because i simply forgot the base init call.

But there is no keyword to "demand" or contractually force the derived method to call the base equivalent. So if you forget, you're going to have a bad time in some cases where you need to call the base. Or other times where you call base and the derived class didn't need to because it overrides all the base values rather than some of them.

Is there a way to make this more robust/strict so such forgetfulness does not occur.

9

Because same effect could already be achieved by a standard pattern of empty overridables.

If all of your non-sealed methods have empty body, it would not matter if they are called from overridden method. Use method template pattern to seal your implementation, and leave only empty methods exposed for override:

public sealed override void callMe() {
     before();
     overrideMe();
     after();
}

protected virtual void overrideMe() {
   // left empty to be optionally implemented in children
}

Seal methods with mandatory calls, and implementor won't be able to circumvent those.

  • 2
    @DavidArno That problem wasn't freshly introduced by this change. It applied to changes in the base version of Init in the original problem statement just as much. To quote the Wikipedia page you linked to: "The fragile base class problem is a fundamental architectural problem of object-oriented programming systems". – Sebastian Redl Jul 24 '18 at 7:48
  • 1
    @DavidArno the goal here is to establish a compiler enforced contract of child-parent communication. It is pretty strict and obvious. Please suggest an example of parent class change that would break a child in a non-obvious manner. – Basilevs Jul 24 '18 at 8:12
  • 2
    I don't think that child breaking parent is better than parent breaking child. And the point about coupling applies to both original (init) and this (empty method) implementations. – Basilevs Jul 24 '18 at 12:00
  • 1
    @DavidArno: The frailty you're pointing at is irrelevant for OP's case where he wishes to enforce proper usage in derived classes. He's not happy if it just builds, he's happy if the derived class does exactly what its base class stipulates. If you wish to enforce this type of stipulations (which he does), and you've already created derived classes, then you either should no longer touch the base class (at least not its stipulations for derived classes), or accept the fact that you need to revisit your derived classes to ensure that your changed stipulations are still being followed. – Flater Jul 24 '18 at 12:56
  • 2
    @DavidArno: I could claim that any interface is similarly fragile. Any change you make to it is liable to break the classes implementing the interface. On principle, it's the exact same thing. But you need to consider that interfaces are intended specifically because they are a set of enforced rules that need to be followed. A base class which hosts the public methods and forces its derived classes to implement protected override methods inherently puts a joint responsibility on the base class and its derived classes. It's not a flaw if it's an explicit intention. – Flater Jul 24 '18 at 13:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.