Decorators v Inheritance: Forcing subclass to call super() ?

M

Michael Strorm

Hi,

I have some classes that are designed such that the subclass
implementations of foo() *must* call the super class implementation of
foo().

What is the best way of forcing this? I read somewhere that this is
poor design; but constructors not only allow it, they *require* it!

One solution I read is to use a decorator class, but this sounds like
gross overkill, and would probably make things even more error-prone.

Any thoughts?.. all help appreciated, thanks.

- MS
 
T

Tor Iver Wilhelmsen

I have some classes that are designed such that the subclass
implementations of foo() *must* call the super class implementation of
foo().

This cannot be enforced (virtual methods and all that), only
documented for the developer, with ominous warnings about what would
happen if the rule isn't followed. Remember to use the terms "warts"
and "first-born son", they always go well with such things. <g>

Basically your only option is to make foo() final.

(I don't know how far they intend to go with 1.5's annotations, but
this looks like a candidate for an annotation like this:

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface MustCallSuperMethod {
}

This would then be checked by the compiler.

The problem is that a subclass author could "forget" to add that
annotation, meaning subclasses one level further down don't have to
call the superclass method.
What is the best way of forcing this? I read somewhere that this is
poor design; but constructors not only allow it, they *require* it!

Constructors are not virtual, and are called in a special way
(invokespecial instruction) different from other methods
(invokevirtual, invokestatic). Also, the call to the superclass
constructor is inserted by the compiler.
 
T

Thomas Weidenfeller

Michael said:
What is the best way of forcing this? I read somewhere that this is
poor design; but constructors not only allow it, they *require* it!

Yes, it is bad design, you can't force the use of your class in that
way. You superclass should be robust, and should work as long as an
overridden method keeps the contract of the original method. And
regarding the behavior of constructors, they are not methods.

You probably have broken up some algorithm in a poor way. So you would
need to go back a few steps and have a look at what you actually want to do.
One solution I read is to use a decorator class, but this sounds like
gross overkill, and would probably make things even more error-prone.

Decorator? Do you need *dynamically* attached additional behavior? If
not, this is simply the wrong pattern.

Consider some redesign, using template and hook methods.

/Thomas
 
M

Michael Strorm

Thomas Weidenfeller said:
Yes, it is bad design, you can't force the use of your class in that
way.

I guessed that much; but if it were possible, would it be a good idea
to use the feature?
You superclass should be robust, and should work as long as an
overridden method keeps the contract of the original method.

That's a good point, but consider this; for the overridden method to
keep the contract of the original method would either require lots of
cut-and-paste code from the original, or calling the super method
anyway.
You probably have broken up some algorithm in a poor way. So you would
need to go back a few steps and have a look at what you actually want to do.

It's a tree check. All nodes need to check themselves and their
parent; non-leaf nodes are subclassed, and need to check their
children recursively. The solution is to either C&P or to call the
super() method.
Decorator? Do you need *dynamically* attached additional behavior? If
not, this is simply the wrong pattern.

I don't remember what this was; I thought about this having posted the
message, and it does seem a bit weird.

- MS
 
M

Michael Strorm

Tor Iver Wilhelmsen said:
Basically your only option is to make foo() final.

I don't understand how that would force the subclass method
(Subclass.foo()) to call the super method (super.foo() AKA
Superclass.foo()). If you're suggesting that the subclass would have
its own, differently-named method (e.g. Subclass.fooExtend()) that
would call the final super.foo(), then that doesn't work.

What I want is to use dynamic dispatch to automaticaly call the right
version of foo(); making foo() final would require either:-

* lots of un-OO class-checking in Superclass.foo(), *OR*
* calling different methods depending on the object type; again,
very non-OO

The problem is that a subclass author could "forget" to add that
annotation, meaning subclasses one level further down don't have to
call the superclass method.

They could make it recursive? Anyway, I don't know enough about Java
to say what they should and shouldn't do...
Constructors are not virtual, and are called in a special way
(invokespecial instruction) different from other methods
(invokevirtual, invokestatic). Also, the call to the superclass
constructor is inserted by the compiler.

Any reason the compiler couldn't enforce that super() was called
somewhere within a given method if it had been marked accordingly?
That deals with the problem at an early stage in the compilation, and
the runtime needs know nothing about it.

- MS
 
V

Virgil Green

Michael said:
I don't understand how that would force the subclass method
(Subclass.foo()) to call the super method (super.foo() AKA
Superclass.foo()). If you're suggesting that the subclass would have
its own, differently-named method (e.g. Subclass.fooExtend()) that
would call the final super.foo(), then that doesn't work.

If the method is final, subclasses can't override it (though they could
overload it), but the method is available as part of the interface to an
object of the subclass. This means that any call of the method would
automatically be a call of the supertype's method.
What I want is to use dynamic dispatch to automaticaly call the right
version of foo(); making foo() final would require either:-

* lots of un-OO class-checking in Superclass.foo(), *OR*
* calling different methods depending on the object type; again,
very non-OO

From your suggestion that class-checking would be required, I assume you
want to force a call to the supertype *and* you want to do additional
processing that is unique to the subtype. Correct?

This came to mind, but I haven't thought a lot about it. Perhaps your
supertype has a public final method that calls an abstract implemention.
Subclasses would be expected to implement the abstract method, but would not
be allowed to override the public final method. Code follows, and I'm eager
to see how it gets picked apart.

public class TestImpl {

public static void main(String[] args) {
BB b = new BB();
b.mydo();
}
}

abstract class AA {
public final void mydo() {
System.out.println("mydo");
mydoimpl();
}
abstract protected void mydoimpl();
}

class BB extends AA {
protected void mydoimpl(){
System.out.println("mydoimpl");
}
}

Programmers who subclass AA are forced to provide a mydoimpl()
implementation of some sort. They can't provide a mydo() implementation that
overrides the superclass (but could overload it).

The first problem I see is that there is nothing to force the programmer to
keep the mydoimpl() method protected. They could expose it as public.

Anyway, that's just what I thought of at the moment.
 
P

P.Hill

Michael said:
It's a tree check. All nodes need to check themselves and their
parent; non-leaf nodes are subclassed, and need to check their
children recursively. The solution is to either C&P or to call the
super() method.

It seems to me that Virgil Green's solution of
(1) make the primary method final,
but
(2) having an extra place to add some more functionality,
goes very well in this case.

final boolean checkFamilyTree() {
checkSelf();
result = checkMother();
if ( result == false ) return result;
return checkChildren();
}

/**
* ONLY OVERRIDE THIS METHOD IF WORKING WITH POLYGAMIST FAMILIES :)
*/
boolean checkMother() {
mother.checkSelf();
}

/**
* Override this method when you have something to do further down the
tree.
* the default implementation does nothing and assume all is well.
*/
boolean checkChildren() {
return true;
}

But given all that, isn't checking down and up at each node
redundant? If you check mom as a child of grandma, then
check grandma, mom and child when checking mom,
then check mom as the parent of child, then child, you've
checked mom THREEE times.

Also, why do I need multiple types in a tree, I certainly
have done these kind of things with a little salting of non-OO
and have one type with potential for NULL

boolean checkMom() {
// no problem, if no mother, aka the root node.
if (mother == null) return true;
mother.checkSelf();
}

boolean checkChildren() {
if (children == null ) return true;
Iterator iChild = children.iterator();
while ...
}

Not so ugly is it?

-Paul
 
D

Dale King

Tor said:
This cannot be enforced (virtual methods and all that), only
documented for the developer, with ominous warnings about what would
happen if the rule isn't followed. Remember to use the terms "warts"
and "first-born son", they always go well with such things. <g>

Basically your only option is to make foo() final.

(I don't know how far they intend to go with 1.5's annotations, but
this looks like a candidate for an annotation like this:

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface MustCallSuperMethod {
}

This would then be checked by the compiler.

The problem is that a subclass author could "forget" to add that
annotation, meaning subclasses one level further down don't have to
call the superclass method.

You could make it inherited, but the bigger issue is that it is too easy
to violate this. Would the following code pass the compiler:

if( false )
{
super.method();
}

It calls the super method but in unreachable code. While it is obvious
to the compiler that this is unreachable, it is not difficult to make
code unreachable that the compiler could not figure out.

So basically we are still at the fact that it is pretty much unenforceable.
 
T

tzvika.barenholz

This basically calls for use of the template method design pattern.

foo() in the superclass should be final. it's implementation should
invoke something like
beforeFoo()
foo code....
afterFoo()

where before and after are the template methods and are designed to be
overridden. They may be abstract in the superclass.

the subclass implementation will put all the code that comes before
calling foo in the before etc.
 
M

Michael Strorm

P.Hill said:
It seems to me that Virgil Green's solution of
(1) make the primary method final,
but
(2) having an extra place to add some more functionality,
goes very well in this case.

Mmm... this seems to be creating a growing list of methods as we move
down the subclasses, each having different names, making OO difficult
(?)
But given all that, isn't checking down and up at each node
redundant?

No, I'm just checking the children and the immediate parent. The check
was for the integrity of the tree, and only does the immediate parent
and immediate children for a given node; and this *did* catch a bug
(see 'Why isn't there a comparator for equals()', this group, also
17/4/05).
Also, why do I need multiple types in a tree

Because the entire program is based around a tree where certain types
can only have certain types of children and parents, do certain
things, and so on. To only have one type would make enforcing this
extremely difficult (IMHO).
boolean checkMom() {
// no problem, if no mother, aka the root node.
if (mother == null) return true;
mother.checkSelf();
}

boolean checkChildren() {
if (children == null ) return true;
Iterator iChild = children.iterator();
while ...
}

This is pretty much what I'm doing at present; leaf-node types don't
bother checking their children, but do themselves and parents;
non-leaf types extend this to include child-checks (direct link, and
recursive subtree check).

- MS
 
M

Michael Strorm

It's an interesting idea. Bearing in mind my brain has temporarily
filled up, I'm not sure if it suits my needs, but I think it does.

The major question is whether the overhead, and increase in code
complexity is worth it. In this case, I think I'll stick with keeping
it simple for this app, but it's worth bearing in mind.

BTW, I think that's the template pattern; oddly, I also heard about
this in the other thread I started in this group ("Why isn't there a
comparator for equals() ?"), but for a distinctly different purpose.

- MS
 
T

Thomas Weidenfeller

Michael said:
BTW, I think that's the template pattern; oddly, I also heard about
this in the other thread I started in this group ("Why isn't there a
comparator for equals() ?"), but for a distinctly different purpose.

You heard it in this thread, too (Message-ID
<[email protected]>), but you did chose to
ignore it. To me this sounds very much as if you are just after
arguments to keep your current sub-optimal design, instead of
considering something more robust. Well, thanks for wasting my time.


/Thomas
 
M

Michael Strorm

Thomas Weidenfeller said:
You heard it in this thread, too (Message-ID
<[email protected]>), but you did chose to
ignore it.

Strange. I replied to that particular message, and responding to most
of the points within. I'm not sure how that was possible if I
"ignored" what you said.

Firstly, you did mention the template; I didn't recognise the name, so
I had it at the back of my head to investigate later. I later found
out what it was (turned out the template was essentially what most
people were suggesting, and something I'd inadvertantly "invented"
myself not so long ago). The one that stuck in my head was from the
other thread because it was using the same pattern to solve a
different problem.
To me this sounds very much as if you are just after
arguments to keep your current sub-optimal design, instead of
considering something more robust.

Secondly, why is my design sub-optimal?
To use the template, the parent class would have to be abstract, and
thus I could not create instances of it.

Are you claiming that *any* class hierarchy where we wish to expand
the behaviour of a concrete instantiable class, but *still* benefit
from the behaviour defined in the parent is "sub-optimal"?

The bottom line is that I did not "ignore" your advice, I read it and
realised that whilst the pattern may have uses, it wasn't what I was
looking for (see previous paragraph).
Well, thanks for wasting my time.

That wasn't my intention; but it's for you to judge whether your time
was wasted or not.

- MS
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top