ArticleS. DeanWampler.
ShouldYouEverOverrideConcreteMethods [add child]

Should You Ever Override Concrete Methods?


A lot of people have pointed out gotchas when overriding concrete classes in Java. For example, properly overriding methods like clone() and equals() can be tricky. (Joshua Bloch's books Effective Java Programming Language Guide and Java Puzzlers: Traps, Pitfalls, and Corner Cases explain the issues better than I can.) In fact, some people recommend that you declare all concrete classes final for this reason.

The same general issue is true for methods. Consider the so-called Call Super anti-pattern. Although I'm thinking of Java at the moment, I don't see any reason why these arguments wouldn't apply in most languages.

Suppose I implement the following base class:


public class Worker {
public void start() {
setup();
}
public void finish() {
teardown();
}

private void setup() {...}
private void teardown() {...}
}


Now suppose I require anyone who overrides the class and calls start() or finish() to call the super method, e.g.,


public class SpecialWorker extends Worker {
public void start() {
super();
// do something special
}
public void finish() {
// do something extra special
super();
}
}


This is considered an anti-pattern because there is no way to force implementers of derived classes to "follow the rules". It is far more reliable to use the Template Method pattern:


public abstract class Worker {
public void start() {
setup();
doStart();
}
public void finish() {
doFinish();
teardown();
}

protected void doStart();
protected void doFinish();

private void setup() {...}
private void teardown() {...}
}

...

public class SpecialWorker extends Worker {
protected void doStart() {
// do something different
}
protected void doFinish() {
// do something innovative
}
}



This is better because it ensures the correct behavior; setup() and teardown() will always be called and subclasses only have to worry about their unique additions. The only downside is that subclasses must implement all the protected methods, not just override the public methods they might really be interested in changing. I see this as very much analogous to the arguments Michael Feathers blogged about recently on Designing Away Preconditions; using the Template Method makes it harder for users to do the wrong thing.

Sure, there are real-world contingencies when you have to do this:

So, given these arguments, I can't think of any examples when it would be a good design to subclass concrete methods. This design constraint seems to be a more useful restriction than making concrete classes final. Am I wrong?


!commentForm

 Fri, 8 Sep 2006 11:37:52, Aaron Korver,
So basically, you're stating the same thing as.."Favor encapsulation over inheritance". Which is what Mr. Bloch points out in his chapter in Effective Java.

I assume you mean his chapter entitled Favor Composition Over Inheritance. Yes, I think the principles are sound. That also seems to be the general trend in OO design over the years; de-emphasize inheritance in favor of composition. Of course, I'm not opposed to inheritance, but I find my designs (in Java at least) tend to consist of this inheritance idiom: an interface, partially implemented by an abstract helper class that implements common state and behavior in the form of template methods, then concrete "leaf-node" classes (not necessarily declared final) that extend the abstract class. I just don't find myself doing much more inheritance than that. However, it's more a rule of thumb than a "principle". I think the method "rules" I blogged about are probably more close to "principles". -- Dean
 Sat, 9 Sep 2006 16:23:13, Ravi Venkataraman,
I find that my designs too rarely use the deep inheritance trees that one sees in standard Java APIs and other open source software. My classes are generally no more than two layers deep, just like Dean mentioned. I used to wonder whether this was an aberration or whether the OO community (especially Java's pseudo-OO one) overuses inheritance. I am beginning to think that there is overuse of inheritance by current practitioners.

Given that inheritance is touted as one of the key advantages of OO technology (along with encapsulation, abstraction and polymorphism), does this imply that OO is over-hyped? Encapsulation, abstraction and polymorhism are easily achieved without the OO paradigm; it seems that inheritance is not much needed, so why use OO design techniques?

I think one of the reasons that deep hierarchies don't work, in practice, is because they are more rigid than the world needs, which is more fluid. When you're working in static languages like Java, where you declare once and forever what a "Person" or "Account" is, it's easy to forget that an autonomous object in one context becomes a collection of smaller objects in another context. The details we care about change from moment to moment. Should every "Person" object contain 0-N address references, the pay rate, a manager reference, the medical history, etc. Obviously, some of those things are integral to "Person" in some contexts, but not others. So, shallow hierarchies give us the flexibility we need, IMHO. -- Dean
 Sun, 10 Sep 2006 07:41:42, Michael Feathers,
I often override concrete methods just to get something working. It's a great way to get 'Fast Green Bar', but then I refactor the overrides away to make the design cleaner.

I find the hardest things to explain are these temporary techniques. There are things I do in the process of work which often cause my pair partner to say "well, that's bad design" and then I have to reply "well, we're not done yet."
 Sun, 10 Sep 2006 13:54:47, Sebastian K├╝beck,
Ravi! I think it's a little bit drastic to ban something (inheritance) just beacuse it's being abused.
Thinking this way, you wouldn't find much left, not only in IT. ;-)
For me, a major principle of OO ist that there's no silver bullet in programming, instead, derive
what you need from existing things. So consequently neither inheritance nor encapsulation are silver bullets.
The choice should be driven from your specific requirements, not your prejudice. ;-)


 Tue, 12 Sep 2006 03:36:11, Neil Greenwood, Problem with abstract Worker class?
I think you forgot to make the start() and finish() methods final in your abstract Worker. If you don't do this, you still allow the incorrect behaviour.

True, but I'm not that religious about it. ;) I assume that anyone using my code is a professional (stupid assumption, I know...) and that occasionally there will be good reasons to override concrete methods, reasons that I'm not clever enough to think about in advance. So I would rather leave the option open. -- Dean