2017-02-06 10 views
2

私のAndroidアプリケーションでは、非常によく似たクラスがあります。FooAFooBと呼びましょう。これらのクラスのそれぞれについてJavaで静的ファクトリメソッドと定数を使って生成

、私はテーブルの列の定数を含むスキーマのクラスを持っている - FooASchemaFooBSchema

public final class FooASchema { 

    public static final String TABLE_NAME = "foo_a_table"; 
    public static final String COL_CATEGORY_ID = "category_id"; 
    public static final String COL_PROPERTY_A = "property_a"; 
    public static final String COL_PROPERTY_B = "property_b"; 
    // COL_PROPERTY_C = ... 

} 


public final class FooBSchema { 

    public static final String TABLE_NAME = "foo_b_table"; 
    public static final String COL_CATEGORY_ID = "category_id"; 
    public static final String COL_OTHER_PROPERTY_A = "other_property_a"; 
    // COL_OTHER_PROPERTY_B = ... 

} 

両方FooAFooB作成するために私を可能にし、静的なファクトリメソッドを持っていますそれらはCursorを使用して:

public static FooA from(Cursor cursor) { 
    int categoryId = cursor.getInt(cursor.getColumnIndex(FooASchema.COL_CATEGORY_ID)); 
    String propertyA = cursor.getString(cursor.getColumnIndex(FooASchema.COL_PROPERTY_A)); 
    String propertyB = cursor.getString(cursor.getColumnIndex(FooASchema.COL_PROPERTY_B)); 
    // int propertyC = ... 

    return FooA(id, propertyA, propertyB, ...); 
} 


public static FooB from(Cursor cursor) { 
    int categoryId = cursor.getInt(cursor.getColumnIndex(FooBSchema.COL_CATEGORY_ID)); 
    int otherA = cursor.getInt(cursor.getColumnIndex(FooASchema.COL_OTHER_PROPERTY_A)); 
    // String otherB = ... 

    return FooB(id, otherA, otherB, ...); 
} 

最後に、私はテーブルからデータを取得するために使用する2つのutilのクラスがあります。

public final class FooAUtils { 

    public static ArrayList<FooA> getFooAs(Context context, int categoryId) { 
     ArrayList<FooA> fooAs = new ArrayList<>(); 

     Cursor cursor = MyDbHelper.getInstance(context).getReadableDatabase.query(
       FooASchema.TABLE_NAME, 
       null, 
       FooASchema.COL_CATEGORY_ID + "=?", 
       new String[] {String.valueOf(categoryId)}, 
       null, 
       null, 
       null); 
     cursor.moveToFirst(); 
     while (!cursor.isAfterLast()) { 
      fooAs.add(FooA.from(cursor)); 
      cursor.moveToNext(); 
     } 
     cursor.close(); 
     return fooAs; 
    } 

    // ... 
} 


public final class FooBUtils { 

    public static ArrayList<FooA> getFooBs(Context context, int categoryId) { 
     ArrayList<FooB> fooBs = new ArrayList<>(); 

     Cursor cursor = MyDbHelper.getInstance(context).getReadableDatabase.query(
       FooBSchema.TABLE_NAME, 
       null, 
       FooBSchema.COL_CATEGORY_ID + "=?", 
       new String[] {String.valueOf(categoryId)}, 
       null, 
       null, 
       null); 
     cursor.moveToFirst(); 
     while (!cursor.isAfterLast()) { 
      fooBs.add(FooB.from(cursor)); 
      cursor.moveToNext(); 
     } 
     cursor.close(); 
     return fooBs; 
    } 

    // ... 
} 

あなたはFooA関連のクラスとFooB関連のクラス間のコードのほとんどは非常に似ていることがわかり、特にutilのクラスですることができます - コードはほとんど同じです。

私はこの複製を減らそうと思っています。私はジェネリックスを使用しようとしています(私はそれらについて読んだことがありますが、まだプロジェクトで使用していません)。

たとえば、汎用のutilクラスを使用できるようにしたいと考えています。

public final class FooUtils { 

    public static <T> get(Context context, int categoryId) { 
     ArrayList<T> items = new ArrayList<>(); 

     Cursor cursor = MyDbHelper.getInstance(context).getReadableDatabase.query(
       BaseSchema.TABLE_NAME, 
       null, 
       BaseSchema.COL_CATEGORY_ID + "=?", 
       new String[] {String.valueOf(categoryId)}, 
       null, 
       null, 
       null); 
     cursor.moveToFirst(); 
     while (!cursor.isAfterLast()) { 
      items.add(T.from(cursor)); // ?? 
      cursor.moveToNext(); 
     } 
     cursor.close(); 
    } 

    // ... 

} 

:ここで私は、私はそれを実装することができると思った方法です

public interface BaseSchema { 

    public static final String TABLE_NAME; // can't make this abstract? 

    public static final String COL_CATEGORY_ID = "category_id"; 

} 

public final class FooASchema implements BaseSchema { ... } 


public final class FooBSchema implements BaseSchema { ... } 

をしかし、あなたが見ることができるように、私はT.from(cursor)を行うことができない、と私は抽象的に一定TABLE_NAMEそれを持つことはできませんサブクラスを実装できます。

この方法で静的ファクトリメソッドを呼び出すにはどうすればよいですか?

これに近づき、コードの重複を減らす良い方法はありますか?あなたがするパラメータ化された型を使用することはできません、ジェネリック医薬品で

fooAs.add(FooA.from(cursor)); 

:あなたはクラスの静的メソッドを使用し、form()工場を呼び出すために、クラスのインスタンスを使用していない、あなたの実際のコードで

+0

は、このためのフレームワークやライブラリを使用することを検討してください。あなたがしたいことは、そうすることができません。ジェネリックはインスタンスでのみ動作します。タイプではありません。工場でのこのアプローチを考えてみてください。 – tynn

答えて

1

ジェネリックがコンパイル後に消去されたため、その上のメソッドitems.add(T.from(cursor));を呼び出します。 FooA(サブクラスがFooインスタンスを作成するために実装する必要が一般的な方法と抽象メソッドと抽象基本クラスを導入

  • :あなたのケースでは

    、私はこの問題を扱う二つの方法を参照してください、FooB)。

  • Fooインスタンスを作成するためのインターフェイスを導入して紹介しています。 2つの実装があります。 1つはFooA、もう1つはFooBです。FooUtils.get()メソッドでそのインスタンスを提供できます。

最初のオプションを使用すると、次のことができます。

基本クラス

public abstract class AbstractFooProcessing<T extends Foo> { 

    public abstract T createFooInstance(Cursor cursor); 

    public ArrayList<T> get(Context context, int categoryId) { 
     ArrayList<T> items = new ArrayList<>(); 

     Cursor cursor = MyDbHelper.getInstance(context).getReadableDatabase.query(
       BaseSchema.TABLE_NAME, 
       null, 
       BaseSchema.COL_CATEGORY_ID + "=?", 
       new String[] {String.valueOf(categoryId)}, 
       null, 
       null, 
       null); 
     cursor.moveToFirst(); 
     while (!cursor.isAfterLast()) { 
      items.add(createFooInstance(cursor)); 
      cursor.moveToNext(); 
     } 
     cursor.close(); 
    } 

    // ... 

} 

FooAProcessing

public class FooAProcessing extends AbstractFooProcessing<FooA>{ 

    @Override 
    public FooA createFooInstance(Cursor cursor) { 
     return FooA.from(cursor); 
    } 

} 

FooBProcessing

public class FooBProcessing extends AbstractFooProcessing<FooB>{ 

    @Override 
    public FooB createFooInstance(Cursor cursor) { 
     return FooB.from(cursor); 
    } 

} 

あなたが次のことを行うことができます番目のオプションでは

FooProcessingインタフェース

public interface FooProcessing<T extends Foo> {  
    T createFooInstance(Cursor cursor);    
} 

FooProcessingA

public class FooAProcessing implements FooProcessing<FooA>{ 

    @Override 
    public FooA createFooInstance(Cursor cursor) { 
     return FooA.from(cursor); 
    } 
} 

FooProcessingB

public class FooBProcessing implements FooProcessing<FooB>{ 

    @Override 
    public FooB createFooInstance(Cursor cursor) { 
     return FooB.from(cursor); 
    } 
} 

get()が引数としてFooProcessingファクトリインスタンスをとるようにFooUtilsを更新しました。

public final class FooUtils { 

    public static <T extends Foo> ArrayList<T> get(Context context, int categoryId, FooProcessing<T> fooProcessing) { 
     ArrayList<T> items = new ArrayList<>(); 

     Cursor cursor = MyDbHelper.getInstance(context).getReadableDatabase.query(
       BaseSchema.TABLE_NAME, 
       null, 
       BaseSchema.COL_CATEGORY_ID + "=?", 
       new String[] {String.valueOf(categoryId)}, 
       null, 
       null, 
       null); 
     cursor.moveToFirst(); 
     while (!cursor.isAfterLast()) { 
      items.add(fooProcessing.createFooInstance(cursor)); // ?? 
      cursor.moveToNext(); 
     } 
     cursor.close(); 
    } 
    // ... 
    return items; 

} 

あなたは今、このようにFooUtils.get()メソッドを呼び出すことができます。

... 
FooProcessing fooAProcessing = new FooAProcessing(); 
... 
ArrayList<FooA> fooAs = FooAUtils.getFoo(context, category, fooAProcessing); 
+0

ありがとうございます。また、2番目のオプションの例を提示して、提案していることをよりよく理解することができますか? –

+0

ようこそ。もちろん、それはあまり変わらない。私は第2のオプションコードで更新しました。 – davidxxx

+0

もう一度@davidxxxに感謝します。私が持っていた他の質問には、 'BaseSchema.TABLE_NAME'(現時点では、これが作業をactualyしません)についてでした。各Fooのクラス(すなわち 'FooA'と' FooB')は、関連するスキーマクラスがあり、すべてのスキーマクラスが一定の同じカテゴリID、および(スキーマクラス間で異なる)テーブル名を持っていることを考えると、私はなって一般化できる方法がありますテーブル名とカテゴリIDの列の定数。私はそれが理にかなって願っています;) –

関連する問題