The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

OO: avoiding overriding methods

Say I've got a abstract class called AbstractWidget, and a bunch of subclasses which are concrete implementations of a Widget.  AbstractWidget has a bunch of data fields and an equals() method, like all good Java classes should.  The equals() method compares some of the data fields and returns true or false based on whether two widgets appear to be equal.

However, the concrete subclasses should also have some say as to whether two widgets are equal, because they may contribute properties of their own.

Traditionally, you'd let the concrete subclass override the abstract class's equals() method.  But this concerns me, because I want to ensure that the concrete subclasses *always* run the code in the abstract class's method.  You can ask your developers to always call super(), but there's no way to really enforce this restriction via the compiler.

So, my next thought is to write code like this:

    protected abstract boolean subEquals(Object that);

    public final boolean equals(Object that) {
        return (... && this.subEquals(that));
    }

This would force the subclasses to implement subEquals() and return a value used by the superclass equals() method.  Does this seem appropriate to the rest of you?  To me, something smells wrong about it.  I kinda feel like I'm going out of my way to avoid correct object-oriented design, and that doesn't feel right.  But I can't really pinpoint what's wrong, or how to solve it.

If this is OK, how would you name such a method?  subEquals() doesn't sound right at all.

Thanks for your input!
Ryan Send private email
Sunday, July 17, 2005
 
 
> To me, something smells wrong about it.

Why would they implement that method correctly if they won't implment the other method correctly?

If you have unit tests this case should be easy to prevent.
son of parnas
Sunday, July 17, 2005
 
 
If you make the Base::equals method virtual and document it, your class will be the most useful to whoever wants to make constructive use of it through inheritance and polymorphism. Make that your goal.

If you choose a different solution, you have likely thrown out the most elegant, clear solution for a more kludgy one. All you get in return is that people who already want to abuse and disrupt your design through subclassing, cannot hurt themselves this way with your class. That isn't a very good trade. Don't bother; you aren't their babysitter, and neither is the compiler.
Jesse Millikan
Sunday, July 17, 2005
 
 
I'm with s.o.p, if your developers don't know to call super.equals first, then you have an HR problem, not a technology problem
matt.
Sunday, July 17, 2005
 
 
Ah, don't take this the wrong way, but you are over-engineering this.  Basically (hah), your base class can't ensure that subclasses are correct.  I really mean that as in '_can not_ [period]'.  Heck, a malicious subclass could throw a ThreadDeath exception if it was called say at exactly 01:01:01.041. 

Now, discount the malicious; the incompetent; the uninformed; and the overworked get it done now engineers, and the subclass is written by oh, say, yourself. ;)  Your subEquals thingie is just a bad idea _for the equals method_.  There are (few) cases were this technique is usefull, but I won't get in to that.
Dave
Sunday, July 17, 2005
 
 
Interesting responses so far.

It seems a common practice in C++ to do exactly what your suggesting.

class abstract_widget
{
    equals( ) { return ( mycheck && equals_impl() ) }
    virtual equals_impl( ) { return true };
}

this pattern especialy helps in cases where the specific implementation in base classes should be done in the middle of something.
 
it doesn't stop overriding however, in the child class you can still define a function equals() and override/hide the parent.  makes a right mess when you try and use the class though.
Gorf
Sunday, July 17, 2005
 
 
I believe what is being suggested here is a use of the template design pattern.  Basically, properly used, it increases code reuse, reduces redundancy, and simplifies debugging because it outlines an algorithm without specifying any details.  Then each virtual function it calls has a simple job with no frills attached that can be easily implemented in each sub-class.  I have used this pattern extensively, even in ways similar to the one described here and found it useful.  The only thing to be careful about is that (if it is designed properly) there should be little to no need (or incentive) to override the function in a subclass.
Haertchen
Monday, July 18, 2005
 
 
Thanks for all the responses.  Back to one of my original questions: any good ideas for how to name the virtual method?  subEquals() or equalsImpl() sound kinda weird. ;-)
Ryan Send private email
Monday, July 18, 2005
 
 
One major problem with this technique is it only works once.

By this, I mean, what happens if further down in your hierarchy, somebody else wants to force an override without derived classes changing subEqual? They're kinda stuck.

Personally, I would only be concerned about this if there is substantial logic in Equals that must happen. Then you use the template method pattern, exactly as you're doing it, to guarantee proper pre- and post- processing.
Chris Tavares Send private email
Monday, July 18, 2005
 
 
interface A {
  foo();
}

abstract class A1 implements A {
  foo() { ... // first implementation of foo}
}

abstract class A2 implements A {
  foo() { ... // second implmentation of foo}
}


No method override required.

class B extends A1 {
  ... // uses first implementation of foo
}

class C extends A2 {
  ... // uses second implementation of foo
}
Dino Send private email
Monday, July 18, 2005
 
 
Say you have a prefix and a postfix that should be applied by a number of parent classes to a final string in the child class.  you could structure the program in several ways if you where to use inheritance to do it.

Class A
{
  WrapString( ) { prefix bit + WrapStringImpl() + postfix bit }
  virtual WrapStringImpl() (return "");
}

Class B : A
{
  virtual WrapStringImpl( ) { prefix bit + WrapStringImpl() + postfix bit }
  virtual WrapStringImpl2() (return "");
}

Class C : B
{
 
  virtual WrapStringImpl2{ prefix bit + WrapStringImpl() + postfix bit }
  virtual WrapStringImpl3() (return "");
}

which forces the appropriate string handling (perhaps) but is REALY UGLY from a class interface point of view.  children classes can start to cut out what the parents do, you have a mess of function names.  But if you only have the 2 classes, it all looks so neat!
It also doesn't look so bad if the things you wish to do at each level are different, and so have different implementation names.


However to get away from that pattern, instead you might try

Class A
{
  virtual WrapString( theString ) { return prefix bit + theString + postfix bit }
}

Class B : A
{
  virtual WrapString( theString ) { return prefix bit + A::WrapString( theString ) + postfix bit }
}

Class C : B
{
  virtual WrapString( theString ) { return prefix bit + A::WrapString( theString ) + postfix bit }
}

And Ooops, the programmer called the wrong super function becauae they have to name it explicitly in C++, and it just happens to make sense to make the call in this case.
But at least it's a neat interface.

So what happens when the writer of the child class doesn't know that it's imperritive that you call the super function at a certain point, be it beginning end or middle of their function?  How does one get to that state? 

It seems to be mainly when you have stuffed the hierarchy, so that you have said something like
Class Driver : person, car {}
when what you should have is
Class Driver : person, licenced { set cars; }
Gorf
Monday, July 18, 2005
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz