使用Java中的静态工厂方法和常量进行泛化

时间:2017-02-06 11:50:54

标签: java android generics

在我的Android应用中,我有一些非常相似的课程,我们称之为FooAFooB

对于这些类中的每一个,我都有一个模式类,其中包含表的列的常量 - 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, ...);
}

最后,我有两个用于从表中检索数据的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

如何以这种方式调用静态工厂方法?

有没有更好的方法来解决这个问题并减少代码重复?

1 个答案:

答案 0 :(得分:1)

在实际代码中,您不使用类的实例来调用form()工厂,而是使用类的静态方法:

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

使用泛型,你不能像items.add(T.from(cursor));那样使用参数化类型来调用方法,因为在编译之后擦除了泛型。

在您的情况下,我看到两种处理问题的方法:

  • 引入一个抽象基类,该基类包含公共方法和子类必须实现的抽象方法,以创建Foo实例(FooAFooB)。

  • 保持自己的行动方式并引入界面来创建Foo实例。 您将有两个实现它。一个用于FooA,另一个用于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);
    }
}

FooUtils 已更新,以便get()FooProcessing工厂实例作为参数。

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