3

背景設定

Android で Web サービス データにアクセスしてキャッシュするためのライブラリを作成しようとしていますが、問題が発生しました。

ライブラリによって管理されるすべてのオブジェクトは、基本Entityクラスから継承されます。の任意の派生クラスは、他のフィールドまたは属性Entityを *宣言できます。

Entity基本クラスは、キャッシュ (SQLite またはファイル) または Web サービス (REST または SOAP) から読み取られる属性を返す役割を担います。私の現在の実装では、クラスはリフレクションを使用してこれを実行します。注釈Entityでマークされたすべてのクラス フィールド名を読み取ります。@Attribute

問題

私の実装で抱えている問題は次のとおりです。Entityのような関数を使用してすべての の属性を要求するたびstatic Set<String> getAttributes()に、この関数は実装を作成する必要がありますSet(現在HashSet、高速ルックアップのために使用しています)。

Set<String>すべての属性のリクエストが行われるたびに を割り当てて初期化することは避けたいと思います。

私の見解では、属性セットはオブジェクトEntityではなく、クラス全体に固有のものです。Entityつまりclass Customer extends Entity、すべてのCustomerインスタンスがまったく同じ属性を持ち、それぞれに名前、アドレスなどがあります。

これに対する私の現在の試みは、次のように、クラスのブロックでリフレクションを使用しstatic Set<String> attributesて拡張Entityおよび初期化するすべてのクラスでa を宣言することです。Setstatic

class Customer extends Entity 
{
   private static final Set<String> attributes = new HashSet<String>();

   static
   {
       Entity.populateAttributes(Customer.class);
   }
}

初期化するクラスが与えられると、クラスはそのクラスとその基底クラスの注釈でEntityマークされたすべてのフィールドを検索できます(たとえば、クラスに対して宣言された属性とクラスから継承された属性でセットを埋めます)。@AttributeCustomer extends PersonPerson extends EntityCustomer.attributesCustomerPerson

私が抱えている問題は、attributes拡張するクラスに対してセットが定義されていない可能性がありEntity、コンパイル時にそれを強制したいということです。

SerializableEclipse のインターフェースでこれが行われているのを見てきました。 を実装するクラスを作成すると、クラスSerializable[private] static final long serialVersionUID.

質問

クラスがフィールドを宣言していない場合、クラスにそのSerializable動作を強制し、警告または (より良い) エラーを表示する方法はありますか?Entity

Entity派生クラスの属性名を返す別の方法はありますか?

脚注

*ライブラリによって管理されるべきではないオブジェクト プロパティの用語と、ライブラリによって管理されるべきオブジェクト プロパティ (Web サービスまたは SQLite/ファイル キャッシュから読み取られ、Web サービスまたは SQLite で永続化される)の用語を使用しました。 /ファイルキャッシュ)fieldattribute

編集1

基本的に、私が達成しようとしているのは、一連のEntityの属性* (上記の脚注を参照) を効率的な方法で取得することです。このリストは、ヘルパー クラスがオブジェクト値をデータベースに格納する (CREATE TABLEクエリは属性名と型から推測できます) か、Web サービスに送信するために使用されます。

このライブラリは、Web サービスから値をキャッシュし、ローカル データベース (追加のユーザー入力値が含まれている可能性があり、オブジェクトのサーバー更新値が欠落している可能性があります) を Web サービス経由でアクセス可能なデータと同期するために使用されます。アプリケーションでのフィールドごとのアクセサー/ミューテーターの使用を汎用アクセサー/ミューテーターに置き換えることは意図されていません。

この概念はキー値コーディングとして知られており、多くのフレームワークやライブラリで使用されています。例として、Google 検索で見つけた KVC を使用するライブラリの最初の 2 つの例は、Cocoa と Sproutcore です。参考文献: Apple 開発者ドキュメントおよびSproutcore wiki

KVC は Android 開発でも使用されます。Bundle、KVC を多用しますSQLiteCursorContentValues

4

1 に答える 1

0

派生クラスの定義で静的フィールドの宣言を強制できない可能性があるため、上記のクラスの定義に属性セットとクラスの説明のマップを格納し、属性Entityのセットが派生クラスの場合、Entityコンストラクターで初期化されます。

例として:

public class Entity
{
   private final static Map<Class<? extends Entity>, Set<String>> attributes = new HashMap<Class<? extends Entity>, Set<String>>();

   public static void populateAttributes(Class<? extends Entity> derivedClass)
   {
      //initialize the set of attributes for the derived class and 
      //add it to attributes map with "derivedClass" as key
   }

   static
   {
      populateAttributes(Entity.class);
   }

   public Entity()
   {
      //calling this.getClass() returns the object's actual (derived) class(*)
      if(!attributes.containsKey(this.getClass())
         populateAttributes(this.getClass());
   }

   //rest of class definition (including getAttributes method)
}

public class Customer extends Entity 
{
   @Attribute
   String someAttribute; //will be processed automatically
}

Entitys 属性を取得するコストは、すべてのクラス属性を解析し、それらを保持する新しいセットを作成することから削減されMapますHashMap

インスタンスを作成するコストはEntity、属性に対して行われるチェックによって増加しますMap(s の高速化HashMap)。特定のクラスのセットを初期化するコストは、attributes派生クラス タイプごとに 1 回だけ実行されるため、ごくわずかです。

90% 以上の場合、属性がクラス インスタンスごとに少なくとも 1 回要求されることを考慮すると、これにより、質問で説明されている最初のソリューションよりも全体的なパフォーマンスが向上するはずです。

(*)this.getClass()基本クラスのインスタンス メソッドを呼び出すと、オブジェクトの実際のクラスが返されます。オブジェクトが派生クラスのインスタンスとして初期化されている場合、派生クラスの説明 (Classオブジェクト) が返されます。これに関する質問はすでにここで尋ねられ、回答されています

于 2013-03-11T09:16:39.330 に答える