How should overruling of overloaded functions work?

nobody nobody at mailinator.com
Sun Aug 20 18:08:10 PDT 2006


Søren J. Løvborg wrote:
>> What jumps out when you just look at the function stubs for base and 
>> derived is that Monitorable_stack chose not to override push_many. It 
>> certainly is one of the functions you ought to be interested in 
>> intercepting.
>>
>> The only plausible conclusion is that an inference has been made about 
>> Stack's hidden implementation. It turns out that was exactly the reasoning 
>> behind the article's author. Yet again has to violate "a central OO 
>> principle like implementation hiding" in order to make his point.
> 
> Imagine that push_many was added to the base class in a later version, after 
> Stack was created. Then you have the exact same problem, without making any 
> assumptions about the implementation as such. And the same problem is used 
> to justify the hiding of overloaded methods in C++ (and hence, D), as 
> discussed in the original post.
> 
> The difference between extending a base class and implementing an interface 
> is that if you add a new method to the class, it can introduce subtle bugs, 
> while if you add it to an interface, it's a compile-time error unless you 
> also add it to every implementation of that interface.
> 
> The reason for extending a class is to override "hook" methods to change the 
> functionality of the class, e.g. the paint() method of a GUI control, or the 
> add() method of a list. However, when you do this, you no longer only have 
> to worry about the "public interface" for the class (all the public methods, 
> which users of the class may call), but also about keeping a consistent 
> "protected interface".
> 
> In the public interface, the implementor simply marks the public methods 
> with visibility "public", and he's done.
> 
>     /* Call this to add an object onto the stack. */
>     public void push(Object o)
> 
> The contract for using the public interface is simple: Provide valid 
> argument values, and the method will do what it's supposed to.
> 
> With the "protected interface", correct visibility is the least of the 
> problems. The implementor needs to define exactly which methods may be 
> overriden by subclasses (not neccessarily all of the public methods), and 
> the exact semantics of each method, for instance:
> 
>     /* This is guaranteed to be called exactly once for every object added. 
> */
>     public void push(Object o)
> 
> The contract for using the "protected interface" isn't simple. When 
> subclassing, you need to read the documentation, or you'll introduce hard to 
> find bugs. The contract cannot be enforced by the compiler, nor can it be 
> enforced at runtime.
> 
> So yes, you can produce code that works perfectly, in which you extend 
> classes (obviously -- everybody does it).
> You can also produce code that works perfectly, in which the member 
> variables of all classes are public.
> By eliminating or minimizing both, you might save yourself some trouble. Or 
> you might not.
> 
> I, personally, am not letting go of subclasses just yet...
> 
> Søren J. Løvborg
> web at kwi.dk 
> 
> 

Thanks for the eloquent and well thought out reply. I realized I was out of line 
with my criticism of his second example. From this article a damning quote:

 > Bill Venners:
 >
 > In Effective Java, Bloch offered a good example in which the addAll method
 > in class HashSet calls add on itself. A subclass attempts to track the total
 > number of objects added to the set by counting the objects passed to both
 > addAll and add, but it doesn't work as expected because of addAll's self-use.
 > If someone passes three objects to addAll, the total number of objects
 > increments by six, not three, because addAll calls add three times.
 >
 > http://www.artima.com/intv/issues2.html

In the previous example he only counts in the single add case but in this 
example counting in both cases is incorrect. The correct behavior seems to be 
only possible either by violating the implementation hiding and peeking or 
somehow getting Stack to also grow a length() function magically!

I would certainly quantify this combination of factors as one nasty example of 
safe changes in a base class having undesired effects on a derived class. 
Certainly as you suggested in your first post adding a length function magically 
is actually pretty easy if you only subclass your own classes.

I am also left wondering how often this concern actually manifests itself. Is it 
purely theory with no practice? Would Sun silently slip in something like an 
addAll function to HashSet later? How much has the C library changed over time? 
How relevent is C's past to the future of more recent languages?



More information about the Digitalmars-d mailing list