Issue
I am trying to implement a factory pattern whereby given an object with a type field, I can dynamically get the class to process that object
Here is a simplified example of what I have so far. Basically, There are a series of Spring Service components which implement the Builder interface. They define the TYPE that they can handle. The Factory class will have builders
injected by Spring i.e. all the classes that implement Builder interface.
The Test class is illustrating how that would be used i.e. the parameter to test will have a type field which is used as a lookup to get the correct builder class.
This works ok however I get a lot of Raw type warnings which I am not sure the best way to get rid of.
Is there a better way to go with this? Thanks!
@Service
public class Factory {
// Raw type warning here
Map<Builder.TYPE, Builder> builderMap;
// Raw type warning here
public Factory(List<Builder> builders){
builderMap = builders.stream().collect(Collectors.toMap( Builder::getType, Function.identity() ));
}
// Raw type warning here
public Builder getBuilder(Builder.TYPE TYPE){
return builderMap.get(TYPE);
}
public interface Builder<T> {
TYPE getType();
ProcessedObject process(T object);
enum TYPE {
ONE
}
}
@Service
public class TestBuilder implements Builder<DtoOne> {
@Override
public TYPE getType() {
return TYPE.ONE;
}
@Override
public ProcessedObject process(DtoOne object) {
// ... process
}
}
@Service
public class Test {
private final Factory factory; // omitting constructor code
public void test(Dto testObject) {
// Raw type warning here
Builder builder = factory.getBuilder(testObject.getType());
// Raw type warning here
builder.process(testObject);
}
}
Solution
Updating answer based on comment below:
If I understood correctly, basically you want to use T to restrict the object type in process() method argument.
I can think of two approaches:
- If you have BaseDto which all other Dto objects (DtoOne, DtoTwo etc.) extend, we can write the code as below.
Builder:
public interface Builder<T extends BaseDto> {
TYPE getType();
Object process(T object);
enum TYPE {
ONE
}
}
Factory:
public class Factory<T extends BaseDto> {
Map<Builder.TYPE, Builder<T>> builderMap;
public Factory(List<Builder<T>> builders){
builderMap = builders.stream().collect(Collectors.toMap( Builder::getType, Function.identity() ));
}
public Builder<T> getBuilder(Builder.TYPE TYPE){
return builderMap.get(TYPE);
}
}
That way we can avoid raw type warnings.
- If there is no common base class, then we can pretty much add any type to a builder which is essentially an unknown type (can be anything). In that case we can replace raw type warnings with wildcard as below.
Builder class will remain same. Factory class would look like below:
public class Factory {
Map<Builder.TYPE, Builder<?>> builderMap;
public Factory(List<Builder<?>> builders){
builderMap = builders.stream().collect(Collectors.toMap( Builder::getType, Function.identity() ));
}
public Builder<?> getBuilder(Builder.TYPE TYPE){
return builderMap.get(TYPE);
}
}
Answered By - aatwork
Answer Checked By - Terry (JavaFixing Volunteer)