简体   繁体   中英

Should I add helper/utility methods to my library API in Java?

I am developing a small library in Java that can read an image from a compressed image data source (eg a PNG file) and decode it, returning an Image object. The Image class has various functions named Image.createImage(...) that can create an image from the specified argument.

I have already added several public Image.createImage(...) methods to the library API, one for each data source type: Image.createImage(Path) , Image.createImage(InputStream) , Image.createImage(String) , Image.createImage(byte[]) , ..., to let the user easily get an Image from various data sources without having to type a lot of code.

However this means the Javadoc is duplicated for each method and the API gets larger, so a bit harder to learn, even though the helper methods themselves are quite trivial (eg Image.createImage(byte[]) is simply return Image.createImage(new ByteArrayInputStream(array)); ), so I've decided to redesign my library API design.

I have thought of three different ways to redesign my Image API:

  • Only provide a single image decoding method that takes an input stream, eg Image.createImage(InputStream) , and let the user create an input stream from his data source himself (and perhaps including some example code in the Javadoc, eg "to decode an image stored in a file, you may use Files.newInputStream(Paths.get(...)) ...")
  • Provide a (helper) method for each data source type, eg Image.createImage(ByteBuffer) , Image.createImage(Path) , ... with the Javadoc essentialy duplicated for each method (this is actually the current situation)
  • Design a DataSource class that can be constructed from one of the data source types (eg new DataSource(Path) , ...), and have a single image decoding method that takes a DataSource object, eg Image.createImage(DataSource)

I was wondering which design is the best one.

  • I think that the first one is quite good because it makes the library quite small/lightweight, but the drawback is that the user has to write the "glue code" himself (or copy it from the Javadoc examples), and thus has to know how to get an InputStream from a Path or a resource String. I'm tempted to choose this design.
  • I think the second one isn't so good because the Javadoc becomes very "verbose"/large and it may not be worth adding one-line methods to the API.
  • I think the third one might well be the worst one, because it becomes too complicated for the user to get an Image if he has to read the Javadoc for both Image and DataSource , then find how to create a DataSource , and I also personally find it very "heavyweight"-ish (reminds me of very large frameworks with a huge number of classes and packages, whereas I like to keep my projects small and concise).

If you were to use this library, which design would you prefer? Is the first design the best?

According to Joshua Bloch,

API Should Be As Small As Possible But No Smaller

• When in doubt leave it out ─ Functionality, classes, methods, parameters, etc. ─ You can always add, but you can never remove

Take a look at page 14 from this link

http://www.cs.bc.edu/~muller/teaching/cs102/s06/lib/pdf/api-design

You actual design seems really fine.

Provide a (helper) method for each data source type, eg Image.createImage(ByteBuffer), Image.createImage(Path), ... with the Javadoc essentialy duplicated for each method (this is actually the current situation)

More an API is simple to use, better it is.
Providing method overloading is often an advantage as the client doesn't need to remember many things to apply a processing/create an object with a different flavor.
He/she has to remember only of the method.

Look at the JDK classes in the java.io packages : FileInputStream , FileOutputStream .
These are designed in this way :

public FileInputStream(String name) throws FileNotFoundException {

public FileInputStream(File file) throws FileNotFoundException {

public FileInputStream(FileDescriptor fdObj) {

Of course, if you define many overloaded methods, for example 8 or more, things are different.
Your API may become harder to read, use and maybe error prone.
it doesn't seem the case for you.

Having javadoc to document for each method should not be a problem.
The client has not to learn them by heart.

Besides, javadoc of them should be very similar :

  • Image.createImage(Path)
  • Image.createImage(InputStream) ,
  • Image.createImage(String) , -
  • Image.createImage(byte[])

And the javadoc of these methods may always refer to a common method with the @see or {@link} javadoc annotation.

Finally, as each method uses a distinct type as parameter, we can easily guess the variance between.

You already got good answers, but regarding javadoc: simply make use of linking to other methods, using:

{@link package.class#member label}

Meaning: if you really have a set of related methods that do "the same" require different parameters - then it is good practice to not only avoid code duplication, but to also avoid documentation duplication. So:

/**
 * This is the extensively documented "anchor" method ...
 * @param String path to image as String ...
 */
public void createImage(String path) { ...

/** 
 * See {@link ...
 * and some additional information ...

From a design point of view, it really depends on what is your aim with the library and how far you want to expand it.

  • First option creates a really lightweight library easy to use in multiple occasion, but leaves to the user a lot of the work you know it is needed to be done, plus some you don't really know. It is easy to write and doesn't limit the way you can use it but it is not so easy to use and it might really not be worth it if you just want to factor code you use a lot

  • If you keep it as it is, you will keep a huge (from the outside) library extremely easy to use. But, as other mentioned, might be difficult to mantain.

  • Third case is the more object-oriented in my opinion. You abstract the idea of a DataSource and you can basically create a hierarchy of classes, one for each type of source you need. Easy to extend, easy to use (with proper factory methods). To create a mantainable library I would use this approach.

On the other side, if your only concern is the repetition of the JavaDoc description, you can always use a {@link} tag to write it once and refer it everywhere else

The problem appears to be related to creation . Consider an ImageFactory which supports the multiple data sources. The glue code is in the factory so there's only a single Image constructor.

Perhaps make the constructor of Image private so users will be required to use the factory.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM