我们的JSF web应用程序中有多个搜索页面,它们具有类似的功能:
- 他们有搜索结果(称这些对象为T)
- 有一个包含搜索条件的对象(称此对象为C)
- 他们能够用名称保存搜索条件(将此对象称为S)
因此,这些都使用JSF页面支持bean,它们都扩展了一个抽象类:
public abstract class AbstractSearch<T, C, S> implements Serializable {
例如:
public class FooSearch extends AbstractSearch<Foo, FooCriteria, FooSavedCriteria>
implements Serializable {
我看到的一个问题是,放入T、C和S中的类与另一个T、C或S类不共享超类型。不过,他们确实有类似的方法。例如,所有的S型类都有一个"getName()"方法。我不能用一个通用的超类来获得这个名称。相反,我必须将以下方法放在AbstractSearch类中,并用它包装我的所有对象:
abstract String getNameFromSavedCriteria(S);
然后在每个子类中实现转换方法:
@Override
String getNameFromSavedCriteria(FooSavedCriteria criteria) {
return criteria.getName();
}
这种做法不好吗?页面上到处都是这些转换助手方法。
另一个似乎是奇怪编码的例子。这是一个刻板的公共方法的抽象搜索:
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);
}
}
再说一遍,这是一种糟糕的做法吗?有没有更好的方法来处理这样一个抽象类,它有很多通用功能,但没有很多可操作的通用类?
我还应该注意到,也许在某个时候,我可以将T/C/S重构为所有使用公共子类型的子类型,这样这些转换辅助方法就可以消失了。还我还没有完全做到。
我看不出"抽象搜索的常规公共方法"有什么问题。抽象类定义了抽象方法,这样它们就可以使用它们了。
为了解决你的问题,我认为朝着正确的方向迈出的一大步是定义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不一定需要一个公共类;S可能。
您的代码示例runSearch()
遵循模板方法模式,我认为它没有太大问题。
我也不认为你的类中有3个泛型有什么问题。看看新的Java 8 Stream/Collect/Reduce方法,它们通常有3种类型。
在我的代码中,当我有这么多这样的泛型时,有时我会制作一个标记接口,但这只是因为如果你有几个不同的FooSearch
实现,它们都使用相同或泛型。
如果你查看Android的AsyncTask,你会发现它有3个类参数。
我说,只要这些类在功能上不重叠,就没有理由重构它