usando una servicio nodejs node hacer ejemplo cómo crear con como java api design frameworks exif

java - servicio - ¿Cómo diseñar una API fácilmente extensible con la simplicidad de Enums?



json rest api (2)

Perdón por el título vago; no podía pensar cómo decirlo más claramente. Aquí están los aspectos más destacados de las preguntas:

Reflejos

  • Hacer una pregunta de diseño API sobre la biblioteca ExifTool para Java .
  • Aquí hay un ejemplo de cómo se ve la API actual.
  • Como USUARIO , la API es súper fácil de usar porque simplemente ingresas en los Enumerados los metadatos de la imagen que deseas recuperar.
  • Como DEV , la API es algo desagradable porque no se puede extender fácilmente la clase base con más tipos de Enum para admitir metadatos adicionales que pueden no ser compatibles directamente en la lib.
  • Simplemente predefinir y soportar " todos los metadatos " no es trivial .

Pregunta

Dada esa información de configuración, lo que busco es buscar una manera de predefinir los 30 o 40 indicadores de metadatos más comunes que las personas suelen querer de sus imágenes; ahora todo se define como un Enum , pero la clase no es extensible de esta manera.

Si tomo la ruta "Class-per-Metadata-flag", la extensibilidad será simple, pero la API será mucho menos amigable para usar de inmediato.

Consideraré hacer v2.0 de esta biblioteca Java 8+ si los cierres ofrecen una solución realmente hermosa y simple, pero de lo contrario obviamente preferiría mantenerla compatible con más sistemas (Java 6/7) que menos.

Resumen

Mis objetivos para la biblioteca son "simples de usar y extender". Siento que tengo el aspecto "simple de usar" con la versión 1.x, pero la biblioteca no es fácilmente ampliable y me gustaría corregir eso en el 2.x series.

He estado sentado en la versión 2.x durante más de un año esperando la inspiración para atacar y me ha eludido; Espero que alguien pueda detectar mi error y pueda mover la libra de una manera realmente elegante.

Gracias por el tiempo chicos!


Aquí hay un par de ideas:

  1. Cree una nueva interfaz para representar una etiqueta y reajuste su enumeración para implementarla. O tal vez llame a la nueva Tag interfaz y cambie el nombre de la enumeración a Tags o Tags CommonTags . Luego crea otra clase que implemente la interfaz, permitiendo etiquetas menos comunes.

    El beneficio de este enfoque es que no requiere muchos cambios en su extremo, pero rompe la compatibilidad de la fuente con las versiones anteriores de la biblioteca, y es un poco más complicado.

    public interface Tag { String getName(); Class<?> getType(); } public enum Tags implements Tag { // mostly same as before } public class OtherTag implements Tag { private String name; private Class<?> type; public OtherTag(String name, Class<?> type) { this.name = name; this.type = type; } @Override public String getName() { return name; } @Override public Class<?> getType() { return type; } }

    En su método getImageMeta , en lugar de simplemente llamar a Tag.forName , tendría que construir un mapa de nombres de etiquetas para Tag objetos de Tag.forName :

    ... Map<String, Tag> tagMap = new HashMap<String, Tag>(); for (Tag tag: tags) tagMap.put(tag.getName(), tag); ... while ((line = streams.reader.readLine()) != null) { String[] pair = TAG_VALUE_PATTERN.split(line); if (pair != null && pair.length == 2) { // Determine the tag represented by this value. Tag tag = tagMap.get(pair[0]); ...

  2. O convierta el Tag enum en una clase simple con muchos campos public static final :

    public class Tag { public static final Tag ISO = new Tag("ISO", Integer.class); public static final Tag APERTURE = new Tag("ApertureValue", Double.class); public static final Tag WHITE_BALANCE = new Tag("WhiteBalance", Integer.class); ... // almost everything else the same // Tag constructor should now be public }

    Esto funcionará, excepto la parte donde se inicializa TAG_LOOKUP_MAP . Allí, o necesita volver a enumerar todas las etiquetas o quizás usar el reflejo para obtener todos los campos en la Tag :

    private static final Map<String, Tag> TAG_LOOKUP_MAP; static { for (Field field: Tag.class.getFields()) { if (Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()) { Tag tag = (Tag) field.get(null); TAG_LOOKUP_MAP.put(tag.getName(), tag); } } }

    Sin embargo, puede que ni siquiera necesites hacer esto, ya que aún necesitas hacer el mismo cambio en getImageMeta que mencioné antes, por lo que tu código no necesitará llamar a Tag.forName . Sin embargo, los usuarios de la biblioteca podrían haber estado usándolo.

    Al revés de este enfoque es que mantiene la compatibilidad de fuentes, se ve casi igual desde el exterior (los usuarios todavía usan Tag.ISO , por ejemplo), y los usuarios pueden crear nuevas etiquetas simplemente haciendo una new Tag("ColorMode", Integer.class) . Lo malo es que todavía rompe la compatibilidad binaria y es un poco más complicado de mantener en el lado del desarrollo.

Estoy seguro de que hay otras opciones, pero hay dos que se me ocurrieron.


Las enumeraciones Java no son extensibles, pero pueden implementar interfaces.

A menudo puede obtener lo mejor de ambos mundos al definir una interfaz que los proveedores pueden implementar, y una enumeración que la implementa y contiene instancias comúnmente utilizadas que los usuarios podrán usar directamente:

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; } }

La API que solía aceptar instancias de la enumeración original ahora debe tomar cualquier instancia de la interfaz.

Los usuarios pueden proporcionar sus propias implementaciones utilizando el mismo patrón:

public enum UncommonPet implements Pet { LION; @Override public String talk() { return "Roar!"; } }

Por último, no es necesario que todas las implementaciones sean enums, por lo que en casos más complejos, el usuario puede optar por implementar la interfaz como una clase completa:

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; } }