I want to create the following Strategy Pattern combined with Factory, but I want it to be typesafe. I have done the following till now:
public interface Parser<T> {
public Collection<T> parse(ResultSet resultSet);
}
public class AParser implements Parser<String> {
@Override
public Collection<String> parse(ResultSet resultSet) {
//perform parsing, get collection
Collection<String> cl = performParsing(resultSet); //local private method
return cl;
}
}
public class ParserFactory {
public enum ParserType {
APARSER
}
public static <T> Parser<T> createParser(ParserType parserType) {
Parser<?> parser = null;
switch (parserType) {
case APARSER:
parser = new AParser();
break;
}
//unchecked cast happens here
return (Parser<T>) parser;
}
}
public class Context {
public <T> Collection<T> getResults(String query, ParserType parserType) {
ResultSet resultSet() = getResultSet(query); //local private method
Parser p = ParserFactory.createParser(parserType);
Collection<T> results = p.parse(resultSet)
}
}
In general whatever I attempt, somewhere I will have an unchecked cast. Anyone have an idea how I can refactor the code to be typesafe?
Checking Effective Java I also stumbled upon this pattern:
public final class ParserFactory {
private ParserFactory() {
}
private static class AParser implements Parser<String> {
@Override
public Collection<String> parse(ResultSet resultSet) {
//...
return new ArrayList<>();
}
}
public static final Parser<String> APARSER = new AParser();
}
So now I can use as Ingo suggested
public <T> Collection<T> getResults(String query, Parser<T> p)
as
getResults("query", ParserFactory.APARSER);
Or would this be better with enums?
I'd simply pass a Parser<T>
to the getResults()
method and forget about that factory stuff. Look, if you say:
public <T> Parser<T> createParser(ParserType typ) { ... }
you are promising that the method will create a parser of any type the caller wants. This is only possible in a type safe way with parsers that all return an empty collection. Moreover, you can't return a Parser<String>
from that function, because String
is not the same as any type the caller wanted.
If, however, you write:
public <T> Collection<T> getResults(String query, Parser<T> parser) {
ResultSet resultSet = getResultSet(query); //local private method
Collection<T> results = parser.parse(resultSet);
return results;
}
you have exactly what you wanted: the getResult
method is independent of how the parser works, and yet it returns a collection of the correct type.
And later, instead of
Collection<String> it = (Collection<String>) getResults("query", APARSER);
you say:
Collection<String> it = getResults("query", new AParser());
This is sound and makes sense.
I usually use this format. I know that a lot of people does not like it but no one suggested a better approach so far.
public enum ParserType {
APARSER(new AParser());
private Parser parser; // this should be an interface which is implemented by AParser
private ParseType(Parser parser){
this.parser = parser;
}
public Parser getParserInstance() {
return parser;
}
}
You can pass Class
objects around if you want a new instance every time:
public enum ParserType {
APARSER(AParser.class);
private Class<Parser> parserClass;
private ParseType(Class<Parser> parserClass){
this.parserClass = parserClass;
}
public Parser createParser() {
return parserClass.newInstance(); // TODO: handle exceptions here
}
}
Note: I'm eager to find a better approach so if you have some thoughts please share them in a comment.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With