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!
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:
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:
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:
Here's a couple ideas:
Create an new interface to represent a tag and retrofit your enum to implement it. Or maybe call the new interface
Tag
, and rename the enum toTags
orCommonTags
. Then create another class that implements the interface, allowing for less common tags.Benefit of this approach is that it doesn't require a lot of changes on your end, but it breaks source compatibility with old versions of the library, and is a little more complicated.
In your
getImageMeta
method, instead of just callingTag.forName
, you'd have to construct a map of tag names toTag
objects before hand:Or convert the
Tag
enum to a simple class with lots ofpublic static final
fields:This will work except for the part where
TAG_LOOKUP_MAP
is initialized. There, you either need to list all the tags again or maybe use reflection to get all the fields onTag
:However, you may not even need to do this, since you still need to make the same change to
getImageMeta
I mentioned earlier, so your code won't actually need to callTag.forName
. Users of the library might have been using it though.Upside to this approach is that it maintains source compatibility, looks mostly the same from the outside (users still use
Tag.ISO
, for example), and users can create new tags by simply doingnew Tag("ColorMode", Integer.class)
. Downside is it still breaks binary compatibility and it's a little bit messier to maintain on the development side.I'm sure there are other options, but there's two that occurred to me.