2013-02-28 5 views
7

曖昧なタイトルに申し訳ありません。それをより明確に語る方法を考えることができませんでした。ここでの質問のハイライトは以下のとおりです。簡単に拡張可能なAPIをEnumの簡単な方法で設計するにはどうすればいいですか?

ハイライト

  • ExifTool for JavaライブラリのAPIの設計質問をします。
  • 現在のAPIの外観はan exampleです。
  • USERとして、返すイメージメタデータにEnumsを渡すだけなので、APIは使いやすいです。
  • DEVとして、libで直接サポートされていない可能性のある追加のメタデータをサポートするために、より多くのEnum型で基本クラスを簡単に拡張することができないため、APIは多少厄介です。
  • すべてのメタデータ」の定義とサポートは、単にnon-trivialです。

質問

考えるとその設定情報、私は後に、人々は、典型的には、その画像から必要な30のまたは40の最も一般的なメタデータフラグを事前に定義する方法を見つけようとしているのです何。今のところすべてがdefined as an Enumですが、クラスはこのように拡張できません。

"Metadata-Class-Per-Per-Flags"ルートを使用すると、拡張性は簡単になりますが、APIの使用がずっと使いやすくなります。

私はこのライブラリのJava2.0を作成することを検討します。もしクロージャが本当に美しくてシンプルなソリューションを提供していれば、それ以上のシステム(Java 6/7)私は1.xのリリースで側面「を使用する単純な」釘付けしていると感じますが、ライブラリーは、容易ではありません - ライブラリの

概要

私の目標は、「使用して、拡張するシンプル」です拡張可能で、私は2.xシリーズでそれを修正したいと思います。

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

ありがとうございました!

+0

あなたが最も簡単で、最も簡単で、最も柔軟で強力な拡張を望むなら、[property bag](http://steve-yegge.blogspot.com/2008/10/universal-design-pattern.html)は方法です行く。 –

+0

@MattBall私はその言葉に精通しているわけではありません。そのリンクのPropertyセクションを見ると、リクエスタが望むメタデータを表すすべてのキーを持つMapを渡すだけで、それらのすべてのキーに関連付けられたValuesにライブラリが埋め込まれ、同じMapが返されるようなことを意味しますか? (悪い考えではありません...非常にシンプルでフレキシブルです) –

答えて

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

これは華麗です。私は彼らが自然界にいかに静的であったかは分かりませんでしたが、これは私が望むものを正確に達成するでしょう。 –

2

はここでカップルのアイデアです:

  1. タグを表す新しいインタフェースを作成し、enumを実装して実装します。または、新しいインターフェイスTagを呼び出して、列挙型の名前をTagsまたはCommonTagsに変更します。その後、インターフェイスを実装する別のクラスを作成し、あまり一般的でないタグを許可します。

    このアプローチの利点は、最終的に多くの変更を必要としないことですが、ライブラリの古いバージョンとのソースの互換性が失われ、少し複雑になります。あなたのgetImageMeta方法で

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

    、だけではなく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 
    } 
    

    これは、 eパーツ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つあります。

+0

Mattsは、APIに非常に特化したimplsを使って、あなたが上を行き来することに本当に感謝しています。 –

関連する問題