战略设计模式,泛型和类型安全

时间:2013-11-11 14:39:20

标签: java generics factory-pattern strategy-pattern

我想创建以下策略模式与Factory结合,但我希望它是类型安全的。到目前为止我做了以下事情:

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)
      }

}

一般来说无论我尝试什么,某个地方我都会有一个未经检查的演员。 任何人都知道如何重构代码是类型安全的吗?

检查有效的Java我也偶然发现了这种模式:

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();

}

所以现在我可以使用Ingo建议

public <T> Collection<T> getResults(String query, Parser<T> p)

作为

getResults("query", ParserFactory.APARSER);

或者这会更好用枚举吗?

3 个答案:

答案 0 :(得分:5)

我只是将Parser<T>传递给getResults()方法而忘记了那些工厂的东西。看,如果你说:

public <T> Parser<T> createParser(ParserType typ) { ... }

您承诺该方法将创建调用者想要的任何类型的解析器。这只能以类型安全的方式使用,所有解析器都返回一个空集合。此外,您无法从该函数返回Parser<String>,因为String与调用者想要的任何类型都不同。

但是,如果你写的话:

  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;
  }

你有你想要的:getResult方法独立于解析器的工作方式,但它返回正确类型的集合。

后来,而不是

Collection<String> it = (Collection<String>) getResults("query", APARSER);
你说:

Collection<String> it = getResults("query", new AParser());

这听起来很有道理。

答案 1 :(得分:3)

我通常使用这种格式。我知道很多人都不喜欢它,但到目前为止还没有人提出更好的方法。

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<Parser> parserClass;

    private ParseType(Class<Parser> parserClass){
        this.parserClass = parserClass;
    }

    public Parser createParser() {
        return parserClass.newInstance(); // TODO: handle exceptions here
    }

}

注意:我很想找到更好的方法,所以如果您有一些想法,请在评论中分享。

答案 2 :(得分:3)

我赞赏你使用战略模式的愿望;为此+1。我确实认为Ingo的评论很有用。

另外一条评论(摘自Effective Java,第二版):

    switch (parserType) {
        case APARSER:
            parser = new AParser();
            break;
    }

使用Joshua Bloch的话,&#34;这个解决方案显得紧凑而且优雅。&#34;然而,它也可能是脆弱的并且难以维护。一般情况下,您应该尝试不要打开枚举常量,因为每当您这样做时,只要您更改枚举,代码就会中断。

现在正是在枚举中使用抽象方法定义的正确时间,并将所需的代码放在枚举常量中。这样做可以保证您永远不会忘记将所需的枚举关联代码添加到项目中,并确保无论何时添加到您的项目中您的项目都不会中断。

事实上,如果您的目标允许,甚至可能或建议将整个工厂方法移动到您的枚举并让您的枚举类实现策略接口。