How should overruling of overloaded functions work?

nobody nobody at mailinator.com
Sun Aug 20 08:56:23 PDT 2006


Søren J. Løvborg wrote:
> See for instance, "Why extends is evil" at JavaWorld, discussing the 
> "fragile base-class" problem.
> http://www.javaworld.com/javaworld/jw-08-2003/jw-0801-toolbox.html

That article was horrible. It sounds plausible that safe changes in a base class 
might have undesired effects on a derived class but I would need an example. All 
his examples ignored the stubs of the base class. Big surprise ending -- he 
fixes the problem of ignoring the stubs of base classes with an interface.

He says "whenever I violate a central OO principle like *implementation hiding*, 
I end up rewriting that code." Yet the article is really about subtle variations 
of failing to hide implementation. So subtle in fact that the author did not 
even realize the problem:

   class ObviousProblem
   {
     public DataStore data;
   }

   class SubtleProblem extends DataStore
   {
     // does not override /any/ DataStore methods
   }

Now we have:

   ObviousProblem o = new ObviousProblem();
   o.data.invalidate();

   SubtleProblem  s =  new SubtleProblem();
   s.invalidate();

In short extending a class just because you are using it as a backing store is a 
bad idea. A big clue that is-a does not hold is when not a single one of your 
base class' methods make sense for the derived class. Yet despite adding and 
removing random elements makes no sense for a Stack his example is:

   class ArrayList
   {
     public void add( int i, Object o ) { }
     public Object remove( int i ) { }
     public void clear() { }
   }

   class Stack extends ArrayList
   {
     public void push( Object article )
     public Object pop()
     public void push_many( Object[] articles )
   }

Now translating that to a more obvious example an implementation hiding failure:

   class StackEquivalent
   {
     public ArrayList data;
     public void push( Object article )
     public Object pop()
     public void push_many( Object[] articles )
   }

Using /any/ of ArrayList's functions on a Stack object will invalidate it. Not 
just clear(). The /exact/ same situation applies to StackEquivalent.



Now the second implementation hiding failutre is not about exposing more than 
one needs but rather less:

   class Stack
   {
     public void push( Object article )
     public Object pop()
     public void push_many( Object[] articles )
   }

   class Monitorable_stack extends Stack
   {
       private int high_water_mark = 0;
       private int current_size;

       public void push( Object article )
       {   if( ++current_size > high_water_mark )
               high_water_mark = current_size;
           super.push(article);
       }

       public Object pop()
       {   --current_size;
           return super.pop();
       }

       public int maximum_size_so_far()
       {   return high_water_mark;
       }
   }

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.

Given the pattern that all his examples ignored the stubs of the base class one 
can only conclude that he needs to use interfaces to be able to think clearly 
about what certain classes expose. If it works for him then I am happy he 
figured it out. However his stub blindness has nothing fundamental to do with 
inheritance. In short I suspect he has misinterpreted "The fragile base-class 
problem".



More information about the Digitalmars-d mailing list