Designed Inheritance
Martin focused on Designed Inheritance protecting users from sharp knives, but compatibility is the real issue at stake. Experts vs. non experts aside, Designed Inheritance helps API designers keep their promises.
If I modify a non final class in my library, I have to anticipate every possible way existing users may have already extended my class and ensure my change doesn't alter the behavior of their code. In the case of a widely used API like the JDK, you simply can't make a change if the remotest possibility of breaking someone exists, even if you're fixing a bug. When it comes to using the JDK, if someone can, someone did. You need a compelling reason to force users to modify and recompile working code.
Which approach is best? Neither. As with most design debates, it depends.
If you write a library where you don't have control over client code and you need to maintain compatibility, steer clear of unnecessary maintenance headaches and practice Designed Inheritance. Use interfaces generously so users don't need to extend your classes in the first place. If you allow users to extend a class, don't call non final methods from your constructor--you risk calling user code before their constructor executes. Many of the idioms in Effective Java target this audience.
If you're writing internal or application code, don't waste too much time defending against yourself and don't over design. Strike a balance between effort and usability and maintainability. Leave your classes open; don't bother with too many interfaces. You can always refactor later.
And don't bother taking sides.
10 Comments:
I've been reviewing Josh's book and it strikes me that he is writing from the library designer's perspective. The only problem is, there is one library designer and millions of users. So why would the library designer's interests EVER win out ? Seems to me a lot of what open source is about is being able to fix or improve any part of a system, not just the parts the designer thought would be changed. If there are mistakes in the code (and there always are), there are probably mistakes in the design, including (perhaps especially) what was made private.
First, ensuring your library can evolve, improve performance, and stand the test of time is in your users' best interests.
Second, it's a lot easier to go from private to public than public to private.
Third, Open Source is an orthogonal issue.
To me, those are secondary. IMO the primary issues with a library are:
1. That it's cost/benefit to me can be evaluated quickly.
2. That it covers the use cases I need correctly.
Think of 1. multiplied by the number of people who use the library. Say your class takes an extra hour to understand. Now you have a million programmers using your class. That's a million wasted hours. IMO this is what the library designers should worry about.
Unfortunately, hiding the implementation frequently interferes with understandability, because the programmer creates a facade for the private objects which in effect, makes a new api for the object. It also bloats the interface of the class with the private object. Also, anytime you prohibit inheritance or fail to allow access to contained objects that should be weighed against, would it be better if the person used copy/paste ?
Also, re: open source, the Sun model is producer/consumer for libraries, right ? The open source model means anyone can change a class and if it is a good change it will probably get added to the library. So it doesn't really matter what is private or overridable or subclassable.
If there are changes that break existing code, why not just deprecate the old member functions ?
Compatibility is just as much of an issue for Open Source projects. If I need to upgrade to a newer version of your library which contains a bug fix, I'd rather not modify my code, Open Source or not.
I'd rather not have to, but if you deprecate the old stuff, I might not have to. Also, if I needed something that was private, I would have had to recreate that functionality, so if you make it public, the time has already been spent.
To me, open source seems to be to the other extreme from the JDK, where you have completely redesigned products (for example Tomcat) all the time.
BTW, I was going to see what Martin Fowler would say, but I see there are no comments on his site.
Tomcat? It implements the Servlet API. And the source for the JDK is available. You can change it, rebuild it, and use it internally if you like. Like I said, Open Source is a separate issue from whether you should make a class final.
Yes, Tomcat implements the Servlet API, but it has gone through two complete rewrites and the Servlet API has changed a lot too. True you can change the JDK, but again a million developers changing it doesn't make sense. I guess feedback is starting with the more open process of Mustang, but it seems to me that a lot of mistakes were made early on (for instance the Swing class hierarchy) and if there is a "don't break client code at all costs" attitude, then these will never get fixed. As an example, could you spin up a jsr to redo any existing library to "favor composition over inheritance" and create interfaces and skeletal implementations as in collections ?
Let's also not forget security. Final plays a key role.
Nice synopsis, Bob :)
Nice synopsis, Bob :)
(Sorry about the duo-post)
Post a Comment
<< Home