The Artima Developer Community
Sponsored Link

Weblogs Forum
Java API Design Guidelines

34 replies on 3 pages. Most recent reply: Jan 5, 2006 2:32 PM by Geek Alot

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 34 replies on 3 pages [ « | 1 2 3 ]
Sebastian Kübeck

Posts: 44
Nickname: sebastiank
Registered: Sep, 2005

Re: Java API Design Guidelines Posted: Jan 1, 2006 11:36 PM
Reply to this message Reply
Advertisement
Terje,

you are absolutely right. There are no guidlines in my posting. I wrote about what I think of bad APIs and
not how to write good ones. I doubt that it's possible
to write a common, easy to use set of guidlines hat
guarantees good API design.
It depends on so many things specific to the functionality
it provides. It's not too complicated to write a API providing
some mathematical functionality or low level IO functionality. It's almost impossible in other areas (business related stuff that is used in various domains).

Sebastian

Geek Alot

Posts: 2
Nickname: geekalot
Registered: Jan, 2006

Re: Java API Design Guidelines Posted: Jan 4, 2006 7:03 PM
Reply to this message Reply
Thanks for the interesting posting! Here are a few more comments on that perennial topic of "What are interfaces good for anyway":

I agree that a package containing a beautiful set of interfaces but no factory methods that return instances of those interfaces or classes that implement those interfaces is confusing to the programmer. Such a package is only useful if there are providers that offer implementations. The trouble is that often there are many providers and many implementations to choose from and configuring your program to use the right one can be confusing. E.g. java.sql or org.xml.sax are useful only in an environment containing real implementations.

For some packages, like org.xml.sax, one can imagine an ultimate implementation and wish we just had that and didn't have to worry about the complexity in having 23 different XML parsers, while for others, like java.sql and java.util.Map, there really can be no ultimate implementation and we just have to live with the complexity.

You mention that if String were an interface one would have to worry that length() might return a negative number. I don't buy this. A well documented interface contains an informal specification of what the classes that implement that interface represent and what the methods of that interface are supposed to do. A length() method in an interface should specify the meaning of negative value or specify that it is not allowed. Of course that specification is informal and someone could implement the interface incorrectly, but then the specification of a class method also informal and someone could implement that incorrectly as well. The only difference is that a final class may have only one incorrect implementation while an interface may have many incorrect implementations!

You mention implementations that cheat and cast parameters with interface types to the non-public implementation class. That is a general problem with O-O languages, packages containing classes meant to be implemented together, and methods of one class that accept abstract objects of a related class.

For example, java.sql.Connection.rollback() takes a java.sql.Savepoint as a parameter. That Savepoint parameter must have been created by java.sql.Connection.setSavepoint() on the same connection used to issue the rollback. The implementation of rollback() is justified in casting the Savepoint into its private implementation class. That is not cheating, that is just the way the game is played. You cannot supply just a Savepoint implementation and expect it to work with other Connection implementations. You have to supply the whole works: Connection and Savepoint (and PreparedStatement and ...).

Interfaces can evolve, they can evolve by extension. The new version of an interface can simply extend the old version. The trouble is that users don't like having to type an ugly interface names like java.sql.Connection-3-0, even though careful dependency mavens think that it is great. One solution is: Don't use Interfaces in API's, use them in SPI's.

The API can be a set of final classes which act as a bridge to the underlying classes that implement particular versions of the SPI interfaces. E.g. A java.sql.Connection could be a simple bridge class that would provide all of the JDBC 3.0 functionality on top of either java.sql.spi.Connection-1.0, java.sql.spi.Connection-2.0 or java.sql.spi.Connection-3.0. If the driver only supports JDBC 2.0, the new 3.0 methods would raise an exception.

Might this same trick handle serialization?

Cheers!

Jaroslav Tulach

Posts: 2
Nickname: jtulach
Registered: Dec, 2005

Re: Java API Design Guidelines Posted: Jan 5, 2006 2:02 AM
Reply to this message Reply
> The API can be a set of final classes which act as a
> bridge to the underlying classes that implement
> particular
> versions of the SPI interfaces. E.g. A

I share this view. In my opinion and experience it really solves most of the problems with backward compatible evolution. Btw. http://openide.netbeans.org/tutorial/api-design.html#design.apiandspi

> You mention that if String were an interface one would
> have to worry that length() might return a negative
> number. I don't buy this. A well documented interface
> contains an informal specification of what the classes
> that implement that interface represent and what the
> methods of that interface are supposed to do.

Well, if you use final classes for API and interfaces for SPI, then you get not just informal specification, but also a way to enforce it. The final classes can assert that the callbacks from the SPI return reasonable values.

> For example, java.sql.Connection.rollback() takes a
> java.sql.Savepoint as a parameter. That Savepoint
> parameter must have been created by
> java.sql.Connection.setSavepoint() on the same connection
> used to issue the rollback. The implementation of
> rollback() is justified in casting the Savepoint into its
> private implementation class. That is not cheating, that
> is just the way the game is played. You cannot supply
> just a Savepoint implementation and expect it to work with
> other Connection implementations. You have to supply the
> whole works: Connection and Savepoint (and
> PreparedStatement and ...).

I would consider this to be bug in the design of the API. Why there is a rollback(Savepoint) method on Connection? There should be Savepoint.rollback() method and then there would be no clash between interface and implementation types.

Gregg Wonderly

Posts: 317
Nickname: greggwon
Registered: Apr, 2003

Re: Java API Design Guidelines Posted: Jan 5, 2006 12:21 PM
Reply to this message Reply
> Interface+skeleton doesn't really give the ability to
> evolve because you can only add methods to the skeleton,
> not the interface. This leads to a fairly messy situation
> where code that wants to call the extra methods has to use
> instanceof and a cast, and be prepared to deal with the
> case where the object is not in fact an instance of the
> skeleton class.

I think it depends on your use of interfaces. I favor creating a new interface with the needed functionality, adding a third interface which is just a container interface which extends the old interface (or defines the needed methods from the skeleton if there was no previous interface) and the new interface. I then change the skeleton to implement that new interface and move on.

Future enhancements require a new tree.

The real issues come out of how many times you decide to do this before you recognize that you're not done with the design of this API yet and you need to stop publishing an interface until you finish it.

You guys did this for JMX remoting and while not pretty, it allowed the RemoteException additions to be hidden from the local implementations by overriding all methods without the declaration of the RemoteException.

I'm still not excited by that design, but it does work to provide the needed functionality with a common base interface.

Geek Alot

Posts: 2
Nickname: geekalot
Registered: Jan, 2006

Family Polymorphism Posted: Jan 5, 2006 2:32 PM
Reply to this message Reply
> I would consider this to be bug in the design of the API.
> Why there is a rollback(Savepoint) method on Connection?
> There should be Savepoint.rollback() method and then there
> would be no clash between interface and implementation
> types.

Good point. I'll guess that the rationale was that since rollback() modifies that global state of the transaction, they wanted to make it a method on the Connection rather than on the Savepoint. However that rationale doesn't apply to Connection.releaseSavepoint() which effects only the Savepoint.

A better example is when there is a true binary operation (not like my rollback example where one of the operands can be inferred from the other) where the operand interfaces either don't have a way of externalizing the "real" value of the operand or it is inefficient to do so.

For example, java.sql.PreparedStatement.setClob() accepts a java.sql.Clob. While one can imagine an implementation of setClob() that uses the Clob methods to extract characters from the argument Clob and pass them in as the argument of the statement, the preferred implementation, when the Clob and the Statement are associated with the same database and/or Connection, is to avoid the extraction and insertion and just pass the reference.

This problem has been dubbed "Family Polymorphism" by Erik Ernst and in his 2001 ECOOP paper with that title he presents some examples http://www.daimi.au.dk/~eernst/papers/fampol.ps.gz.
Related papers include the earlier Harper and Lillibridge "A type-theoretic approach to higher-order modules with sharing" in POPL 1994 http://www.hpl.hp.com/personal/Mark_Lillibridge/Sharing/popl94.PDF
and the more recent Odersky and Zenger "Scalable component abstractions" in OOPSLA 2005 http://lamp.epfl.ch/~odersky/papers/ScalableComponent.pdf.

Flat View: This topic has 34 replies on 3 pages [ « | 1  2  3 ]
Topic: Mixins2 Previous Topic   Next Topic Topic: Mixins: Something Else You Can't Do With Java Generics?

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use