Restricting Overriding (was: More On Multiple Inheritance)

Hans-Dieter Dreier Ursula.Dreier@ruhr-uni-bochum.de
Wed, 07 Apr 1999 23:50:49 +0200



Matthew Tuck schrieb:

> Anyway, to prevent overriding it is enough to make it private or final.
> Since it can never be dynamically dispatched to, all the "override"s I
> can think of may as well be defined as another (possibly non-final)
> method in the subimpl.

"Another" means that it needs a different name, right?So "final" just prevents
overriding?
That is not exactly the same as static dispatch.
Then why shouldn't a final method be called by dynamic dispatch?

There are these categories:
- Being able to override
- Being able to access (maybe using qulification)
- Dynamic/static dispatch policy

IMO these are orthogonal.
Therefore each should be controlled by a facility (keyword) of its own.
For maximum flexibility, there would be two keywords for each:
One to turn the facility on, the other to turn it off, plus a (user defineable)

default on a per-module basis. Everyone would then have its way.
Compatibility and "mixability" would be preserved.

> >> Often what the author really wants is to restrict overriding of their
> >> implementation.  Hence they won't care if you write a whole new
> >> implementation.
> > That really would break the spirit of OOP. No one would want to restrict
> > overriding as long as it's *safe*. That's the point.
>
> Why would it?  OOP is partly about contract, and if the impl designer
> wants to ensure the code works in exactly that way, it's their right, in
> exactly the same way as they can refuse access to a member.  You don't
> do a final unless there's a good reason for it.
>
> You're perfectly entitled to write another impl not based upon this if
> you don't like the terms and conditions.

Yes, of course, but why can't we build an extension rather than duplicating
those parts that are OK to us?

> >> Other sorts of override modifiers are possible as well - prefix only,
> >> suffix only, surround only, etc.  CLOS does something like this with its
> >> overriding.
> > This would be useful for message handlers. Often they simply add their
> > own code to the standard impl.
>
> Indeed.  I have been writing some code recently that involved an
> Approval Cycle for a document being approved from person to person.  It
> was object oriented in the sense that it had a top-level, then
> subclassed by an approval structure (e.g. from A->B->C or A->B AND A->C
> then D waiting on B & C), then for the specific document database and
> then for the specific document type.
>
> Essentially I often wanted to write final methods, since I was defining
> a framework.  Also, I often wanted to restrict overriding in different
> ways such as prefixing.
>
> Think of it this way.  I know it has to be used that way.  It doesn't
> make sense otherwise.  Hence, when I subimpl it, I want to know if I
> use it wrong.  What if I accidentally override the wrong method?  Forget
> to dispatch to the parent method at the end of the overridden method?

I understand that "prefixing" would automatically call the parent method at the
end.Sometimes this is nice, but one cannot say that it is generally usable.
What about the return value? Will it yield yours or the parent's?
What about postfixing and infixing? Conditional pre/in/postfixing?

IMO these cases cannot sensibly be handled in a general way.
Maybe a helpful feature could a language feature that says:
"Dear compiler, if I'm overridden, would you please issue a warning if I'm not
called
somehow/somewhere in the overriding method".
The user must be able to switch this warning off for subsequent compiles once
he has recognized it.

> >> Can you think of any examples where you've wanted to use final?
> > Not really, but maybe that's just because I haven't written many classes
> > that are intended to be used by others.
>
> Information hiding is useful even for one person.

Well, then I'd use "private" (or protected or s.th like that).

> > To a great extent I'm feeling
> > uncomfortable with your opinion because it reduces (if not to say
> > destroys) locality of code. If you can't tell any longer how something
> > might work by looking at it and the parts it expressly uses because you
> > can't prevent some unknown client to change its operation (for himself
> > and his clients) in some arbitrary manner, you'd have to consider at lot
> > more code to find bugs. I consider that a huge drawback.
>
> I'm fully in favour of impl final, and always have been.  I just think C
> virtual is silly in the way it works, and that final isn't always
> enough.

IMO they chose to do it like they did because of two reasons:

1. Be compatible to C (if you don't use it)
2. Be space/time efficient. This is why "virtual" is not the default.

BTW, what is silly about the way it works?

> >> In fact, the "its either private or totally public" stance of most
> >> languages these days is one of my dear issues, but that's another story.
> > That definitely is a problem, but I see no reason why we shouldn't change
> > this. The Eiffel approach to this lookes pretty flexible to me, for
> > example.
>
> I don't recall anything about Eiffel in this domain, can you elaborate?

   class ACCOUNT   feature {BANK}
      balance : INTEGER;
      ...
   end --ACCOUNT

Above, only clients of ACCOUNT who are of declared to be of class BANK
or who inherit from BANK may directly access balance.
Instead of BANK there can be a list of "trusted" classes for each single
feature
in a class. This is a lot more flexible than the C++ approach.

See
http://www.progsoc.uts.edu.au/~geldridg/eiffel/advance-intro/s2/#s2.4
for details.

> > IMO it is better designed if the parent
> > author provides virtual "hooks" that you may or may not override (he may
> > provide a default function). This way can can make sure that his vital
> > code always gets executed.
>
> I agree - I use this technique extensively.  My real qualm with virtual
> (I just realised) is that the subimpl writer can write the method
> without realising that it won't be dynamically dispatched to.  Final
> doesn't allow this possible bug.

Well if he is so used to dynamic dispatch he may set his default (see
suggestion above) to "virtual".But there may be other programmers (like me) who
are more concerned about controlling
locality of code (and performance). I'd like to see that those also get their
way.

> > Another possibility for the parent impl author is to restrict access to
> > all critical parts of the system. Maybe that is even better because (if
> > designed right) you can easily see where an overriding might possibly
> > occur. If designed right, it's more flexible as well, but optimisation
> > will be more difficult.
>
> Can you elaborate on what you mean by this?

Please see my note on categories at the start of this postingas well as the
Eiffel export mechanism mentioned above.
Optimisation will be more difficult if the dispatch policy isn't declared
with the dispatched feature because it will then depend on derived classes as
well.
If the compiler detects a possible case for dynamic dispatch while static
dispatch is still chosen,
it may query the user whether he wants to make the base class feature virtual.

> --
>      Matthew Tuck - Software Developer & All-Round Nice Guy

--

Regards,

Hans-Dieter Dreier
(Hans-Dieter.Dreier@materna.de)