Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to design an easily-extensible API with the simplicity of Enums?

Sorry for the vague title; couldn't think of how to word it more clearly. Here are the highlights of the questions:

Highlights

  • Asking an API design question about the ExifTool for Java library.
  • Here is an example of what the current API looks like.
  • As a USER, the API is super-simple to use because you just pass in Enums for the image metadata you want back.
  • As a DEV, the API somewhat sucks because you cannot easily extend the base class with more Enum types to support additional metadata that may not be supported directly in the lib.
  • Simply pre-defining and supporting "all the metadata" is non-trivial.

Question

Given that setup information, what I am after is trying to find a way to pre-define the 30 or 40 most common metadata flags that people typically want from their images; right now everything is defined as an Enum, but the class is not extensible this way.

If I go the "Class-per-Metadata-flag" route, the extensibility will be simple, but the API will be a lot less friendly to use out of the box.

I will consider making v2.0 of this library Java 8+ if closures offer a really beautiful and simple solution, but otherwise I'd obviously prefer to keep it compatible with more systems (Java 6/7) than less.

Summary

My goals for the library are "simple to use and extend" - I feel I have nailed the "simple to use" aspect with the 1.x release, but the library is not easily extensible and I'd like to correct that in the 2.x series.

I have been sitting on the 2.x release for over a year waiting for inspiration to strike and it has eluded me; I am hoping someone can spot my mistake and I can move the lib forward in a really elegant way.

Thank you for the time guys!

like image 236
Riyad Kalla Avatar asked Feb 28 '13 22:02

Riyad Kalla


1 Answers

Java enums are not extensible, but they can implement interfaces.

You can often get the best of both worlds by defining an interface that providers can implement, and an enum that implements it and contains commonly used instances that the users will be able to use directly:

public interface Pet {
    public String talk();
}
public enum CommonPet implements Pet {
    CAT("Meow!"),
    DOG("Woof! Woof!");

    private final String cry;

    CommonPet(String cry) {
        this.cry = cry;
    }

    @Override
    public String talk() {
        return cry;
    }
}

The API that used to accept instances of the original enum should now take any instance of the interface.

Users can provide their own implementations using the same pattern:

public enum UncommonPet implements Pet {
    LION;

    @Override
    public String talk() {
        return "Roar!";
    }
}

Finally, there is no requirement that all implementations should be enums, so in more complex cases the user can choose to implement the interface as a full-fledged class:

public class Parrot implements Pet {
    private String phrase = "Pieces of eight!";

    @Override
    public String talk() {
        return phrase;
    }

    public void teach(String phrase) {
        this.phrase = phrase;
    }
}
like image 155
Andrea Avatar answered Nov 01 '22 20:11

Andrea