Reputation: 89
I am fairly new to java and below are my mapper interface definitions:
public interface CourseMapper {
Course map(JsonObject courseJson);
List<Course> map(JsonArray coursesJson);
}
public interface LocationMapper {
Location map(JsonObject locationJson);
List<Location> map(JsonArray locationsJson);
}
and below are the two classes:
public interface Location {
Long id();
String name();
Location mutate(Long id);
}
public interface Course {
Long id();
String name();
Course mutate(Long id);
}
What I need is based on a key(String), I need to invoke the respective map()
function of respective Mapper
. Example if the key=="location"
, I need to invoke LocationMapper
and if the key=="course"
, I need to invoke CourseMapper
(for gson Mapper)...... and so on.
I tried using Factory Pattern for the same, but it seems that the same doesn't work when the Output is of different type. Would anyone know how to achieve the same without overtly complicating with if-else ladder.
Upvotes: 1
Views: 304
Reputation: 12440
The most simple approach is to have a Map<String, Object>
to store your mappers in:
private Map<String, Object> mappers;
public <T> T getMapper(String key, Class<T> mapperType) {
Object mapper = mappers.get(key);
// code for when mapper == null
if (mapperType.isAssignableFrom(mapper)) {
return (T) mapper;
} else {
// code for when mapper is of wrong type
}
}
As you'll notice however, this gives up static type checks, so any type mismatch will only be found at run time.
A more complicated approach can save (sort of) the static type checks. It basically ties the result type with the string key:
private static class MapperKey<T> {
private final String key;
public MapperKey(String key) {
this.key = key;
}
}
private Map<String, Object> mappers;
public static final MapperKey<CourseMapper> COURSE_KEY = new MapperKey<CourseMapper>("course");
public <T> void addMapper(MapperKey<T> mapperKey, T mapper) {
mappers.put(mapperKey.key, mapper);
}
public <T> T getMapper(MapperKey<T> mapperKey) {
Object mapper = mappers.get(mapperKey.key);
// code for when mapper == null
return (T) mapper;
}
This approach however depends on users using the predefined constants for MapperKey
s, or creating the correct keys (correct type parameter for the respective key).
And finally, if you can replace your CourseMapper
and LocationMapper
interfaces with this:
public interface Mapper<T> {
T map(JsonObject courseJson);
List<T> map(JsonArray coursesJson);
}
And if you only need one mapper per output type, you can use the output type as a key:
private Map<Class<?>, Mapper<?>> mappers;
public <T> void addMapper(Class<T> outputType, Mapper<T> mapper) {
mappers.put(outputType, mapper);
}
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> outputType) {
return (T) mappers.get(outputType);
}
Upvotes: 3