Question

In languages with dynamic typing, the use of polymorphism may trigger errors on a super-class.

I will try to explain my question with a simple example: Supposing a language with dynamic typing (like ECMAScript) and the following class structure:

diagram

class A{
    private idA;
    public A(){
        idA=0;
    }
    public foo(){
        update();
        if (this.idA!=3) Throws new Exception(" What is happening? ");
    }
    private update(){
        this.idA = 3;
    }
}
class B extends A{
    private idB;
    public B(){
        super();
        idB=0;
    }
    public foo(){
        super.foo();
        // Any operation between A::update and B::update()
        if (this.idB!=0) Throws new Exception("hmmm, that could not happend!");
        update();
    }
    private update(){
        this.idB = 5;
    }
}

In this very simple example, when i create an object of the class B, B::foo() call the parent A::foo(), which call "update". The object is an instance of B, so the "update" functions called is B::update, after that, in B::foo, the update function is again called (B::update). The final result is that A::update is never called, and idA still 0.

The class A work correctly when used alone, but after to extend it with B, the function foo() fail.

What is the correct solution this problem:

1) Force the class A to call A::update , that mean an ugly code every call to his own function (protect the super-class):

A::foo(){
    A::update();
    if (this.idA!=3) Throws new Exception(" What is happening? "); 
}

2) B::update is an extension of A::update, so B::update must call itself the parent function (prepare the sub-class, and deal with problems):

B::foo(){
    super.foo();
    ... // Any operation that must be performed between A::update and B::update
}
B::update(){
    super.update();
    this.idB = 5;
}

But in this case is the A::foo which call update, not the B::foo. That mean others problems.

3) Any other solution.

As a summary:

How to protect the super-class code against polymorphism?

  • Add protections into the super-class.
  • Deal with these problem creating the child-class
  • The language must do that! (do not know if it is possible with dynamically typed languages)

I am looking for a very theoretical /canonical solution to this question.

EDITED: to take the problem out of the constructor and clarify some points.

Was it helpful?

Solution 2

The base class should protect itself from harmful overrides. In keeping with the open/close principle, it should be open to extension but closed to modification. Overriding update is a harmful modification of the base class's intended behaviour. In your example, there is no benefit in overriding update because both A::update and B::update are private methods that deal with private variables. There isn't even an expectation that they should be executed together judging by your exception in B::foo. If B::update was named differently, there wouldn't be anything wrong with your implementation. It would probably be OK anyway: since no language I know of will let you override a private method, B::update could hide A::update rather than overriding it.

Depending on the language, you can limit which methods can be overridden in different ways. Some languages require an indicator (a keyword or attribute usually) that a method can be overridden, others to show that it can't. Private methods are generally not overridable, but not all languages have access modifiers at all, and everything is effectively public. In this case you would have to use some kind of convention as suggested by @PoByBolek.

tl;dr: Children have no business with their parents' privates.

OTHER TIPS

It's generally considered a very bad practice to call instance methods, and especially virtual instance methods from within a constructor exactly for this reason (but also for the reason that the object isn't done being "initialized" yet).

3) Any other solution.

Doc, it hurts when I do this.

Then don't do that!

Seriously, if you need to set IdA in the constructor of A, don't do it by calling update, do it by explicitly setting the value of IdA in the constructor for A.

You're probably not going to like my answer but: convention and disciplin.

Establish conventions for

  • when it is safe for a child class to override a method without calling the parent class implementation,
  • when a child class has to call the parent class implementation of an overridden method,
  • when a child class must not override a parent class method.

Document these conventions and stick to them. They should probably be part of your code; either in form of comments or naming conventions (whatever works for you). I could think of something like this:

/*
 * @final
 */
function shouldNotBeOverridden() {
}

/*
 * @overridable
 * @call-super
 */
function canBeOverriddenButShouldBeCalledFromChildClasses() {
}

/*
 * @overridable
 */
function canBeOverridenWithoutBeingCalledFromChildClasses() {
}

This may help someone reading your code to figure out which methods he may or may not override.

And if someone still overrides your @final methods, you hopefully have thorough testing ;)


I like this answer to a somewhat similar question regarding python:

You could put a comment in there to the effect of:

# We'll fire you if you override this method.

If the language allows one class to call a private method of another class this way, the programmer has to understand and live with that. If I'm understanding your objective, foo and update should be overridden and update should be protected. They would then call the method in the parent class, when necessary. The derived foo method wouldn't need to call update, because calling foo in the parent class would take care of that. The code could work like this:

class A{
    private idA;
    public A(){
        idA=0;
    }
    public foo(){
        update();
        if (this.idA!=3) Throws new Exception("idA not set by update");
    }
    protected update(){
        this.idA = 3;
    }
}
class B extends A{
    private idB;
    public B(){
        super();
        idB=0;
    }
    @Override
    public foo(){
        super.foo();
        // Any operation between A::update and B::update()
        if (this.idB!=5) Throws new Exception("idB not set by super.foo");
    }
    @Override
    protected update(){
        super.Update()
        this.idB = 5;
    }
}

I changed the exceptions to match expectations.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top