Discussions

News: Opinion: Don't call super

  1. Opinion: Don't call super (18 messages)

    Cedric has written about some trials and tribulations with DBUnit. He came across the "I didn't call the super method from within a subclass" problem.

    He snarled on the topic, and some people joined in. Howard Lewis Ship commented on how he uses inheritence less and less, and Vincent Massol discussed The Right Way to extend JUnit.

    Read Cedric and friends at Don't call super.

    At least Cedric had another fun game to play while he was frustrated :)

    Threaded Messages (18)

  2. Another nice game[ Go to top ]

    Another nice game called Yeti Sports:

    http://62.116.30.115/

    click "play online".
    play with mouse.
  3. Wow, some hard-hitting journalism[ Go to top ]

    Hard hitting story here on TSS. Why do these sorts of things get any attention? If I were Cedric, I'd post more and more insane "coding rules" to see exactly what TSS would repost. What's next "Opinion: Java should get rid of semicolons", because some keeps getting compile errors?

    "I didn't call the super method" is not a "problem", it is a "mistake programmers make". It doesn't deserver a coding standard, blog entry or TSS article.
  4. Opinion: Don't call super[ Go to top ]

    It's called learn how to do Object Oriented design.
  5. Opinion: Don't call super[ Go to top ]

    It's called learn how to do Object Oriented design.


    Software design is not a door you unlock and pass through. There's always more to learn, more to define, new techniques to try, evaluate, adopt or discard. Don't be dismissive, especially of people like Cedric who have the understanding of the issues sufficient to recognize and describe the problem.

    The basic problems of software development have not significantly changed with the advent of OO. They will likely not change with the adoption of AOP (Aspect Oriented Programming). At issue is dealing with complexity. Fundamentally, a subclass that invokes and overrides the methods of a super-class is the same as two unrelated objects interacting with each other. In effect, the interface of the sub-class gains the "surface area" of the super-class, plus some additional invisible-to-the-outside interactions and dependencies. Complexity.

    In earlier languages such as Objective-C, garbage collection was non-existent, so object allocations had to be kept to a minimum. This favored the use of inheritance over aggregation. In addition, in the early 90's, memory was in short supply; the trend was to use inheritance, and "gift" subclasses with convienience methods. Again, this favored fewer, more monolithic objects.

    GC in Java, and an abundance of memory (well, you can never have enough) have addressed these problems but uncovered others. If the interaction between a super-class and a sub-class is too complex, bugs can be easily introduced. Is it necessary to invoke super.foo()? If so, do I do it before I do my special processing, or after? Or somewhere in the middle? What methods does it call and when?

    If you consider the super class to be "providing a service" to the sub-class, then you can break apart the inheritance hierarchy. The service consumer (which was the subclass) has a much clearer contract with the service provider (which was the superclass). The APIs may change slightly, but your end up replacing a fuzzy interdependency with a clearly deliniated set of contracts.

    Along the way, you gain the ability to perform meaningful unit testing, since you can describe your contracts in terms of interfaces, not particular classes, and use mock implementations in your tests.

    For example, if you have a super-class that provides generic database access, and a sub-class that leverages the methods of the super-class to perform specific database queries, then you need a database to test. Depending on the implementation, you may need an entire application server!

    If you break it apart into consumers and providers, you can stub out the database access provider service and get real testing for your consumer.

    That's what I call Object Oriented Design. And that was what Cedric was getting at. Or perhaps I'm just reading my own HiveMind-flavored view into his statements (which parellel my own observations).
  6. Opinion: Don't call super[ Go to top ]

    This actually is a well documented OO design technique. You even have code checkers that help you spot this tricky little B.

    It is called "Design for Extension". Code checking software like CheckStyle can help you do it.

    from the checkstyle website (http://checkstyle.sourceforge.net/config_design.html#DesignForExtension):
    <quote>This API design style protects superclasses against beeing broken by subclasses. The downside is that subclasses are limited in their flexibility, in particular they cannot prevent execution of code in the superclass, but that also means that subclasses cannot corrupt the state of the superclass by forgetting to call the super method. </quote>

    I'm not saying we should just throw away inheritance (there are case where it can be very usefull), but if even Joshua Bloch found this issue important enough to dedicate a whole section (item 15: desing and document for inheritance or else prohibit it) in his great book (Effective Java) to it, I guess it is worthwile reflecting on the subject, isn't it?
  7. Opinion: Don't call super[ Go to top ]

    But I live in an apartment in Manhattan - who am I supposed to call if my heat goes off!?!?!

    Kidding aside, this seems like a minor thing but I also think Cedric (et al) have a good point. It's an excellent design technique to have explicit methods that help sub-classes participate properly in a class' lifecycle. I've used this technique myself to great effect, although I tend to think of them as in-class callbacks.


        -Mike
  8. Opinion: Don't call super[ Go to top ]

    Kidding aside, this seems like a minor thing but I also think Cedric (et al) have a good point. It's an excellent design technique to have explicit methods that help sub-classes participate properly in a class' lifecycle. I've used this technique myself to great effect, although I tend to think of them as in-class callbacks.

    >
    >
    >     -Mike

    Isn't this covered in Bertrand Meyer's book? His taxonomy of inheritance coveres probably most cases(unfortunatelly, his recurrent advice is "document heavily").
  9. Opinion: Don't call super[ Go to top ]

    Kidding aside, this seems like a minor thing but I also think Cedric (et al) have a good point. It's an excellent design technique to have explicit methods that help sub-classes participate properly in a class' lifecycle. I've used this technique myself to great effect, although I tend to think of them as in-class callbacks.

    > >
    > >
    > >     -Mike
    >
    > Isn't this covered in Bertrand Meyer's book? His taxonomy of inheritance coveres probably most cases(unfortunatelly, his recurrent advice is "document heavily").

    It's also the GoF Template Method - they call it a "hook" if it has a default imlpementation, otherwise just a template method if it's abstract in the calling class.

    It's very powerful and often abused, when people create superclasses like this:

    public class PurchaseManager {

        public void handlePurchase() {
             checkSecurity();
             doPurchase();
             logTransaction()
        }

        public abstract void doPurchase();

    }


    I've seen it a lot - performing some cross-cutting concern like security, then delegating to the "Real" implementation which has a similar name or a meaningless name like "execute()". Subclass authors are then left clueless as to what they actually have to take care of. Better to break the Real implementation into cohesive pieces that can be declared as separate methods,
    e.g. "changeBalance()", "removeFromStock()", etc, and let the superclass control them. This doesn't just reduce code clutter - it ensures subclasses all behave similarly. Some of the decomposed methods might have defaults, others might be abstract.

    <pedantic>
    Shouldn't this be called "Don't make me call super" instead of "Don't call super" --- it's not my fault if a third-party library forces me to call super.
    </pedantic>
  10. Opinion: Don't call super[ Go to top ]

    There is something in the article that I don't understand. Are there, OO or other, languages where forgetting something doesn't break your code or where you can prevent programmer errors or even where the language knows the difference between forgetting and voluntary not implementing. Isn't that one of the reasons why we test our code?
  11. Opinion: Don't call super[ Go to top ]

    There is something in the article that I don't understand. Are there, OO or other, languages where forgetting something doesn't break your code or where you can prevent programmer errors or even where the language knows the difference between forgetting and voluntary not implementing. Isn't that one of the reasons why we test our code?


    Forget a semicolon, you find out immediately. Forget to call super, you spend three hours compiling, debugging, testing, cursing. Testing will help, but you still won't spot the cause if you aren't aware of an assumption made by a class you're using.

    It's only human to forget details like that - a well-designed framework/language compensates by preventing the need to remember them in the first place, and - when errors do occur - alerting you as soon as possible.
  12. Opinion: Don't call super[ Go to top ]

    Forget a semicolon, you find out immediately. Forget to call super, you spend three hours compiling, debugging, testing, cursing.


    I agree but don't be so negative and curse. You should be positive and enjoy the fact that you finally found the solution :)

    Testing will help, but you still won't spot the cause if you aren't aware of an assumption made by a class you're using.

    I work a lot with VB. So I will give you a Microsoft Best Practice for free. "Never, I repeat, NEVER make assumptions about something you didn't write yourself."

    >
    > It's only human to forget details like that - a well-designed framework/language compensates by preventing the need to remember them in the first place, and - when errors do occur - alerting you as soon as possible.

    In the case of avoiding errors, I agree with you. But in the 'super'-case (witch is not an error, but a design decision) I repeat my comment, there is no soft that can guess my intentions. If you do an automatic include of the super, you get me a lot of unwanted behaviour when I want to rewrite the method from scratch. On the other hand, when you forbid the use of the super, I have to rewrite all the wanted behaviour of the super myself.
  13. Opinion: Don't call super[ Go to top ]

    come on! inheritance is a powerful feature and very useful. The crowd of wanna-be OO programmers is the real danger.
  14. Opinion: Don't call super[ Go to top ]

    Cedric has written about some trials and tribulations with DBUnit. He came across the "I didn't call the super method from within a subclass" problem.


    I dont like the general idea of having to extend TestCase anyhow. Each project that adds funtionality to junit tends to create their own base class extending from TestCase, and if you want to use features from more than one projects then you have to hack around that, or hope that they happen to be in some inheritance hierarchy - i.e. TestCase --> HttpUnitTestCase --> CactusTestCase. Not very convenient.

    The template method is a nice pattern, but given the way junit derivative projects extend each other I wonder how it would have worked for this case. The way it is now all I have to do is override setUp and call super. If it was done using template method then each project would have to come up with a new name for the hook method that the super class would call into, right?

    So:

    TestCase:
    setUp( setUpExtended(); );
    abstract setUpExtended();

    HttpUnitTestCase:
    setUpExtended( setUpExtended2(); );
    abstract setUpExtended2();

    CactusTestCase:
    setUpExtended2( setUpExtended3(); );
    abstract setUpExtended3();

    More project specifics to remember - pretty error prone.

    Br - J
  15. Opinion: Don't call super[ Go to top ]

    I dont like the general idea of having to extend TestCase anyhow.


    You don't need to extend DatabaseTestCase to use DbUnit:
    http://www.dbunit.org/howto.html#noextend

    I rarely do it myself. The main DbUnit functionalities don't lie in this class. Think about this class as a template that shows you how you can invoke DbUnit from your own tests.

    I found that DbUnit integration greatly varies from one project to another. Up to now, I've done it differently for every projects.
  16. Cedric is right on target[ Go to top ]

    As a strong OOA and OOD, I find this type of coding to be a VERY bad idea because he's right, someone will break it. There's no guarentee that super() will be called, and so by defintion there can be no contract that requires it. Method contracts apply only to how a method is called, not to how it's overridden. I've had issues with this for a while and with the no arg constructor. However, there might be something that can be done about it:

    http://www.purpletech.com/junit-patch/index.jsp

    This patch removes the constructor issue. Maybe someone can do something about the setup method's super() req.

    You don't see this type of requirement in the SUN JDK or any other that I know about. Still you can't beat on the JUnit developers too much. It works, and it's free, right?
  17. Cedric is right on target[ Go to top ]

    Bad design is bad design.

    Don't blame inheritance.

    Peace,

    Cameron Purdy
    Tangosol, Inc.
    Coherence: Clustered JCache for Grid Computing!
  18. Opinion: Don't call super[ Go to top ]

    I'm having hard time seeing how virtual methods introduce a breach of a contract. Methods don't break contracts, developers do, that was so perfectly demonstrated by Cedric.

    Finally, if you don't trust yourself and others - declare a method final and sleep well.
  19. Opinion: Don't call super[ Go to top ]

    After careful thinking I can say I have found a cure to the problem.

    If the test driven development were used in this case, it would not be a problem at all. Wrote an extension of TestCase? Be so kind, write a test case, prove it works (and breaks) where it should.