我怎样才能将这个带有多个泛型的可怕抽象 java class 重构为 "better"?

How can I refactor this horrid abstract java class with multiple generics into something "better"?

我们的 JSF 网络应用程序中有多个搜索页面,它们具有类似的功能:

所以这些都使用一个 JSF 页面支持 bean,它都扩展了一个抽象 class:

public abstract class AbstractSearch<T, C, S> implements Serializable {

例如:

public class FooSearch extends AbstractSearch<Foo, FooCriteria, FooSavedCriteria> 
    implements Serializable {

我看到的一个问题是,放入 T、C 和 S 中的 classes 不与另一个 T、C 或 S class 共享超类型。他们确实有类似的方法。例如,所有 S 型 classes 都有一个 "getName()" 方法。我不能使用普通的 superclass 来获取这个名字。相反,我必须将以下方法放入 AbstractSearch class 并用它包装我的所有对象:

abstract String getNameFromSavedCriteria(S);

然后在每个subclass:

中实现转换方法
@Override
String getNameFromSavedCriteria(FooSavedCriteria criteria) {
    return criteria.getName();
}

这是一种不好的做法吗?页面上到处都是这些转换辅助方法。

另一个例子,似乎是奇怪的编码。这是 AbstractSearch 的典型 public 方法:

public void runSearch() {

    if (simpleSearch == true) {
        clearAdvancedSearchFields(); // abstract method
    }        

    populateSearchFromForm(); // abstract method

    try {
        setResults(processRunSearch()); // abstract method
        cacheResults(results); // abstract method
        cacheSearch(search); // abstract method

        if (getResults().isEmpty()) {
            Messages.addGlobalWarn("No results found");
        }

        if (formAutoHide) {
            searchFormVisible = getResults().isEmpty();
        }

    } catch (MyException ex) {
        FacesUtils.addGlobalError(ex);
    }
}

同样,这是一种不好的做法吗?有没有更好的方法来处理像这样的抽象 class,它有很多共同的功能,但没有很多共同的 classes 可以操作?

我还应该注意,也许@某些时候,我可以将 T/C/S 重构为所有使用通用子类型的方法,以便这些转换辅助方法可以消失。仍然......我还没有完全到那里。

如果您查看 Android 的 AsyncTask,您会发现它有 3 个 class 参数。

我说只要那些 classes 在功能上不重叠,就没有理由重构它。

您的代码示例 runSearch() 遵循模板方法模式,我看不出有什么问题。

我也没有真正看到您 class 中的 3 个泛型有问题。看看新的 Java 8 Stream/Collect/Reduce 方法,通常有 3 种类型。

在我的代码中,当我拥有如此多的泛型时,有时我会创建一个标记接口,但这只是因为如果你有多个不同的 FooSearch 实现,它们都使用相同的或泛型。

我看不出 "stereotypical public method of AbstractSearch" 有什么问题。抽象 classes 定义了抽象方法,因此他们已经可以使用它们了。

为了解决您的问题,我认为朝着正确方向迈出的一大步是为 T、C、S 定义边界,以便它们可以在抽象级别上相互使用。

例如:

abstract class Criteria {
    abstract String getName();
    String getNameFromSavedCriteria(SavedCriteria s) {
        return s.getName();
    }
}
interface SavedCriteria {
    String getName();
}
public abstract class AbstractSearch<T, C extends Criteria, S extends SavedCriteria> implements Serializable {

   void foo() {
       System.out.println("Hello. Searching for " + getCriteria().getName());
   }
}

T、C、S 不一定需要通用 class,C & S 可能需要。