Problem
I am trying to design two methods that filter a list
public List<TypeA> filter(List<TypeA> typeAList) {
//duplicated code
//filter typeAList using some parameters in typeA objects
//return filtered list
}
public List<TypeB> filter(List<TypeB> typeBList) {
//duplicated code
//filter typeBList using some parameters in typeB objects
//return filtered list
}
The problem is both the methods have duplicate code except for the filtering part where I access different parameters inside TypeA and TypeB.
Things I tried so far
- I tried making a generic method like this. But this does not support types other than TypeA and TypeB. Encourages someone to call this method with an unintended type.
public <T> List<T> filter(List<T> genericList) {
//duplicated code
if (T instanceOf TypeA)
//filtering code for TypeA
if (T instanceOf TypeB)
//filtering code for TypeB
//return filtered list
}
- Overload with two methods calling a private generic filter method. I felt this ensures unintended calls to the public method, while still using generics to avoid code duplication.
public List<TypeA> filter(List<TypeA> typeAList) {
//call innerFilter(typeAList)
}
public List<TypeB> filter(List<TypeB> typeBList) {
//call innerFilter(typeBList)
}
private <T> List<T> innerFilter(List<T> genericList) {
//duplicated code
if (T instanceOf TypeA)
//filtering code for TypeA
if (T instanceOf TypeB)
//filtering code for TypeB
//return filtered list
}
- Tried to make the two classes implement a common interface and use that interface as the input parameter to my method. But one of the class is third-party and not under my control.
Help needed
I'm really new to design. Want to understand if my reasoning is right behind the approaches. Also looking for suggestions on alternate best approaches to solve this problem. Thanks in advance.
CodePudding user response:
The appropriate structure is not reflective, and does not use instanceof
.
public List<TypeA> filter(List<TypeA> typeAList) {
innerFilter(typeAList, typeA -> isGoodA(typeA))
}
private boolean isGoodA(TypeA a) { ... }
public List<TypeB> filter(List<TypeB> typeBList) {
innerFilter(typeBList, typeB -> isGoodB(typeB))
}
private boolean isGoodB(TypeB a) { ... }
private <T> List<T> innerFilter(List<T> genericList, Predicate<T> pred) {
//duplicated code
//filter genericList using pred
//return filtered list
}
CodePudding user response:
Assume you have this 2 TypeX
interface without inheritance link and with same methods signature.
interface TypeA {
String methodFromA();
}
interface TypeB {
String methodFromB();
}
You could declare an Enum who knows which method has to be called for each TypeX interface.
enum FilterType {
TYPE_A(TypeA.class){
@Override
public <T> void callMethod(T typeX) {
TypeA typeA = (TypeA) typeX;
typeA.methodFromA();
}
},
TYPE_B(TypeB.class){
@Override
public <T> void callMethod(T typeX) {
TypeB typeB = (TypeB) typeX;
typeB.methodFromB();
}
};
Class typeClass;
FilterType(Class typeClass) {
this.typeClass = typeClass;
}
public static FilterType from(Class<?> typeClass) {
return Arrays.stream(values())
.filter(filterType -> filterType.typeClass.equals(typeClass))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("FilterType for class '" typeClass "' not exist")),
}
public abstract <T> void callMethod(T typeX);
}
Finally, in your filter method, you just have to recover the enum instance with the TypeX class and call the appropriated method on it.
class FilterService<T> {
// The class type of TypeX interface
private final Class<T> typeClass;
public FilterService() {
// Recover the class of the generic T
this.typeClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
public List<T> filter(List<T> genericList) {
FilterType filterType = FilterType.from(typeClass); // Will throw IllegalArgumentException if T class isn't handle
genericList.forEach(typeX -> filterType.callMethod(typeX));
//return filtered list
}
}