7

あいまいなタイトルでごめんなさい。それをより明確に表現する方法を考えることができませんでした。質問のハイライトは次のとおりです。

ハイライト

  • ExifToolforJavaライブラリに関するAPI設計の質問をします。
  • これは、現在のAPIがどのように見えるかのです。
  • ユーザーとして、APIは非常に簡単に使用できます。これは、必要な画像メタデータの列挙型を渡すだけだからです。
  • DEVとして、libで直接サポートされていない可能性のある追加のメタデータをサポートするために、より多くの列挙型で基本クラスを簡単に拡張できないため、APIはやや面倒です。
  • 「すべてのメタデータ」を事前に定義してサポートすることは簡単ではありません

質問

そのセットアップ情報を前提として、私が求めているのは、人々が通常画像に求める30または40の最も一般的なメタデータフラグを事前に定義する方法を見つけようとしていることです。現在、すべてが列挙型として定義されていますが、クラスはこの方法で拡張できません。

「Class-per-Metadata-flag」ルートを使用すると、拡張性は単純になりますが、APIはそのまま使用するのがはるかに簡単ではなくなります。

クロージャーが本当に美しくシンプルなソリューションを提供する場合は、このライブラリのv2.0をJava 8+にすることを検討しますが、それ以外の場合は、より多くのシステム(Java 6/7)との互換性を維持したいと思います。

概要

ライブラリの私の目標は「使いやすく拡張する」です。1.xリリースで「使いやすい」という側面を明確にしたと思いますが、ライブラリは簡単に拡張できないため、 2.xシリーズ。

私は2.xリリースに1年以上座って、インスピレーションが得られるのを待っていましたが、それは私にはわかりませんでした。誰かが私の間違いを見つけて、本当にエレガントな方法でライブラリを前進させることができることを願っています。

お時間をいただきありがとうございます!

4

2 に答える 2

6

Java 列挙型は拡張できませんが、インターフェイスを実装できます。

多くの場合、プロバイダーが実装できるインターフェイスと、それを実装し、ユーザーが直接使用できる一般的に使用されるインスタンスを含む列挙型を定義することで、両方の世界を最大限に活用できます。

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

元の列挙型のインスタンスを受け入れていた API は、インターフェースの任意のインスタンスを取る必要があります。

ユーザーは、同じパターンを使用して独自の実装を提供できます。

public enum UncommonPet implements Pet {
    LION;

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

最後に、すべての実装が列挙型でなければならないという要件はないため、より複雑なケースでは、ユーザーはインターフェイスを本格的なクラスとして実装することを選択できます。

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;
    }
}
于 2013-02-28T23:02:23.650 に答える
2

ここにいくつかのアイデアがあります:

  1. タグを表す新しいインターフェイスを作成し、列挙型を改良して実装します。または、新しいインターフェイスTagを呼び出して、列挙型の名前をTagsorに変更しCommonTagsます。次に、インターフェースを実装する別のクラスを作成して、あまり一般的でないタグを使用できるようにします。

    このアプローチの利点は、ユーザー側で多くの変更を行う必要がないことですが、古いバージョンのライブラリとのソース互換性が失われ、少し複雑になります。

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

    getImageMetaメソッドでは、 を呼び出すだけでなく、事前Tag.forNameにタグ名からTagオブジェクトへのマップを作成する必要があります。

    ...
    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. または、列挙型を多くのフィールドTagを持つ単純なクラスに変換します。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
    }
    

    これは、初期化されている部分を除いて機能しTAG_LOOKUP_MAPます。そこで、すべてのタグを再度リストするか、リフレクションを使用してすべてのフィールドを取得する必要があります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);
            }
        }
    }
    

    ただし、前述の に同じ変更を加える必要があるため、これを行う必要さえない場合があるgetImageMetaため、コードで実際に を呼び出す必要はありませんTag.forName。ライブラリのユーザーはそれを使用していた可能性があります。

    このアプローチの利点は、ソースの互換性が維持され、外部からはほとんど同じように見え (Tag.ISOたとえば、ユーザーはまだ を使用しています)、ユーザーは単純に を実行するだけで新しいタグを作成できることnew Tag("ColorMode", Integer.class)です。欠点は、依然としてバイナリ互換性が損なわれることと、開発側で維持するのが少し面倒なことです。

他にも選択肢はあると思いますが、思いついたのは 2 つあります。

于 2013-02-28T23:12:03.410 に答える