8

我想创建以下与 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);

或者使用枚举会更好吗?

4

3 回答 3

5

我只需将 a 传递Parser<T>给该getResults()方法并忘记工厂的东西。看,如果你说:

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

您承诺该方法将创建调用者想要的任何类型的解析器。这只能以类型安全的方式使用所有返回空集合的解析器。此外,您不能Parser<String>从该函数返回 a,因为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());

这是合理且有道理的。

于 2013-11-11T17:03:53.303 回答
3

我赞赏您使用策略模式的愿望;为此+1。我确实认为 Ingo 的评论是正确的。

只是一个附加评论(取自 Effective Java,第 2 版):

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

用 Joshua Bloch 的话来说,“这个解决方案看起来很紧凑,甚至很优雅。” 然而,它也可能很脆弱并且难以维护。通常,您应该尽量不要打开枚举常量,因为无论何时这样做,只要您更改枚举,您的代码就会中断。

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

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

于 2013-11-11T18:12:25.343 回答
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
    }

}

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

于 2013-11-11T14:44:55.337 回答