我怎样才能将这个带有多个泛型的可怕抽象 java class 重构为 "better"?
How can I refactor this horrid abstract java class with multiple generics into something "better"?
我们的 JSF 网络应用程序中有多个搜索页面,它们具有类似的功能:
- 他们有搜索结果(称这些对象为 T)
- 有一个包含搜索条件的对象(将此对象称为 C)
- 他们可以使用名称保存搜索条件(称此对象为 S)
所以这些都使用一个 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 可能需要。
我们的 JSF 网络应用程序中有多个搜索页面,它们具有类似的功能:
- 他们有搜索结果(称这些对象为 T)
- 有一个包含搜索条件的对象(将此对象称为 C)
- 他们可以使用名称保存搜索条件(称此对象为 S)
所以这些都使用一个 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 可能需要。