2017-02-06 7 views
2

In meiner Android-App habe ich einige sehr ähnliche Klassen, nennen wir sie FooA und FooB.Generieren mit statischen Factory-Methoden und Konstanten in Java

Für jede dieser Klassen habe ich eine Schema-Klasse, die Konstanten für die Spalten der Tabelle enthält - FooASchema und FooBSchema:

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 = ... 

} 

Beide FooA und FooB haben eine statische Factory-Methode, die mir ermöglicht zu schaffen sie mit einem 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, ...); 
} 

Schließlich habe ich zwei util-Klassen, die ich verwenden, um Daten abzurufen aus den Tabellen:

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

    // ... 
} 

können Sie sehen, dass die meisten der Code zwischen FooA -related Klassen und FooB -related Klassen sehr ähnlich sind, und vor allem in den util Klassen -, wo der Code fast identisch ist.

Ich möchte versuchen, diese Duplizierung zu reduzieren, und ich habe versucht, dies mit Generika zu tun (ich habe über sie gelesen, aber ich habe sie noch nicht in einem Projekt verwendet).

Zum Beispiel möchte ich in der Lage sein, eine generische Util-Klasse zu haben. Hier ist, wie ich dachte, dass ich es umsetzen könnte:

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

    // ... 

} 

Wo:

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 { ... } 

Aber wie Sie sehen können, kann ich nicht T.from(cursor) tun, und ich kann nicht eine abstrakte Konstante TABLE_NAME haben, dass die Unterklassen können implementieren.

Wie kann ich meine statische Fabrikmethode auf diese Weise aufrufen?

Gibt es einen besseren Weg, dies zu erreichen und die Code-Duplizierung zu reduzieren?

+0

Betrachten wir einen Rahmen oder eine Bibliothek für diese Verwendung. Was Sie tun möchten, ist als solches nicht möglich. Generics arbeiten nur mit Instanzen; nicht mit Typen. Vielleicht sollten Sie diesen Ansatz nur mit Fabriken in Betracht ziehen. – tynn

Antwort

1

In Ihrem aktuellen Code, den Sie nicht über eine Instanz der Klasse verwenden, um die form() Fabrik aufzurufen, verwenden Sie eine statische Methode der Klasse:

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

Mit Generika, können Sie nicht die parametrisierte Typ verwenden, um Rufen Sie eine Methode darauf items.add(T.from(cursor)); seit dem generischen wurde nach der Kompilierung gelöscht.

In Ihrem Fall, ich sehe zwei Wege im Umgang mit dem Problem:

  • eine abstrakte Basisklasse mit dem gemeinsamen Verfahren und einer abstrakten Methode einzuführen, die Unterklassen haben eine Foo Instanz implementieren zu erstellen (FooA , FooB).

  • halten Sie Ihre Art zu tun und Einführung einer Schnittstelle, um eine Foo Instanz zu erstellen. Sie hätten zwei Implementierungen davon. Eine für FooA und eine andere für FooB und Sie könnten eine Instanz davon in der FooUtils.get() Methode bereitstellen.

Mit der ersten Option könnten Sie Folgendes tun.

Basisklasse

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

} 

Mit der zweiten Option könnten Sie Folgendes tun.

FooProcessing Schnittstelle

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

FooUtils aktualisiert, so dass get() als Argument eine FooProcessing Factory-Instanz übernimmt.

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; 

} 

Sie können nun die FooUtils.get() -Methode auf diese Weise nennen:

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

Vielen Dank für Ihre Antwort. Könnten Sie auch ein Beispiel für die zweite Option geben, damit ich besser verstehe, was Sie vorschlagen? –

+0

Gern geschehen. Natürlich ändert sich nicht viel. Ich habe mit dem zweiten Optionscode aktualisiert. – davidxxx

+0

Nochmals vielen Dank @davidxxx. Die andere Frage war "BaseSchema.TABLE_NAME" (im Moment funktioniert das nicht wirklich). Wenn man bedenkt, dass jede Foo-Klasse (dh 'FooA' und' FooB') eine zugehörige Schemaklasse hat und jede Schemaklasse die gleiche Kategorie-ID-Konstante hat und ein Tabellenname (unterschiedlich über Schemaklassen) ist, kann ich das Generieren verallgemeinern die Konstanten für die Spalten Spaltenname und Kategorie-ID. Ich hoffe, das macht Sinn;) –

Verwandte Themen