战略设计模式,generics和类型安全
我想创建以下策略模式与Factory结合,但我希望它是类型安全的。 到目前为止我做了以下事情:
public interface Parser { public Collection parse(ResultSet resultSet); } public class AParser implements Parser { @Override public Collection parse(ResultSet resultSet) { //perform parsing, get collection Collection cl = performParsing(resultSet); //local private method return cl; } } public class ParserFactory { public enum ParserType { APARSER } public static Parser createParser(ParserType parserType) { Parser parser = null; switch (parserType) { case APARSER: parser = new AParser(); break; } //unchecked cast happens here return (Parser) parser; } } public class Context { public Collection getResults(String query, ParserType parserType) { ResultSet resultSet() = getResultSet(query); //local private method Parser p = ParserFactory.createParser(parserType); Collection results = p.parse(resultSet) } }
一般来说无论我尝试什么,某个地方我都会有一个未经检查的演员。 任何人都知道如何重构代码是类型安全的?
检查有效的Java我也偶然发现了这种模式:
public final class ParserFactory { private ParserFactory() { } private static class AParser implements Parser { @Override public Collection parse(ResultSet resultSet) { //... return new ArrayList(); } } public static final Parser APARSER = new AParser(); }
所以现在我可以使用Ingo建议
public Collection getResults(String query, Parser p)
如
getResults("query", ParserFactory.APARSER);
或者用枚举会更好吗?
我只是将一个Parser
传递给getResults()
方法并忘记那个工厂的东西。 看,如果你说:
public Parser createParser(ParserType typ) { ... }
你承诺该方法将创建一个调用者想要的任何类型的解析器。 这只能以类型安全的方式使用,所有解析器都返回一个空集合。 此外,您不能从该函数返回Parser
,因为String
与调用者想要的任何类型都不相同。
但是,如果你写:
public Collection getResults(String query, Parser parser) { ResultSet resultSet = getResultSet(query); //local private method Collection results = parser.parse(resultSet); return results; }
你有你想要的东西: getResult
方法独立于解析器的工作方式,但它返回一个正确类型的集合。
而后来,而不是
Collection it = (Collection ) getResults("query", APARSER);
你说:
Collection it = getResults("query", new AParser());
这听起来很有道理。
我通常使用这种格式。 我知道很多人都不喜欢它,但到目前为止还没有人提出更好的方法。
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; } }
如果每次都想要一个新实例,可以传递Class
对象:
public enum ParserType { APARSER(AParser.class); private Class parserClass; private ParseType(Class parserClass){ this.parserClass = parserClass; } public Parser createParser() { return parserClass.newInstance(); // TODO: handle exceptions here } }
注意:我很想找到更好的方法,所以如果你有一些想法,请在评论中分享。
我赞赏你使用战略模式的愿望; 为此+1。 我确实认为Ingo的评论是现货。
只是另外一条评论(摘自Effective Java,第2版):
switch (parserType) { case APARSER: parser = new AParser(); break; }
用约书亚布洛赫的话来说,“这个解决方案显得紧凑而且优雅。” 然而,它也可能是脆弱的并且难以维护。 通常,您应该尝试不要打开枚举常量,因为每当您这样做时,只要更改枚举,代码就会中断。
这正是在枚举中使用抽象方法定义的正确时间,并使用枚举常量将所需代码放在那里 。 这样做可以保证您永远不会忘记将所需的枚举关联代码添加到项目中,并确保无论何时添加到枚举中项目都不会中断。
事实上,如果你的目标允许,甚至可能或建议将整个工厂方法移动到你的枚举,让你的枚举类实现Strategy接口。