Java 策略设计模式、泛型和类型安全

Java 策略设计模式、泛型和类型安全,java,generics,factory-pattern,strategy-pattern,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

我想结合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)
      }

}

或者使用枚举会更好吗?

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

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
    }

}
public enum ParserType{
APARSER(APARSER.class);
私有类parserClass;
专用ParserType(类parserClass){
this.parserClass=parserClass;
}
公共解析器createParser(){
return parserClass.newInstance();//TODO:在此处处理异常
}
}

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

我只需将
解析器
传递给
getResults()
方法,然后忘记工厂的东西。听着,如果你说:

public <T> Parser<T> createParser(ParserType typ) { ... }
您完全可以得到您想要的:getResult方法独立于解析器的工作方式,但它返回正确类型的集合

后来,而不是

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

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

这是合理的,也是有意义的。

我赞赏您使用战略模式的愿望+1美元。我认为国际非政府组织的评论是正确的

只是补充一点意见(摘自《有效的Java》,第二版):

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


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


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

只要
createParser
方法(或任何其他方法)被认为是凭空创建t(可以说),您就无法保证此类型的安全。(因为它的字面意思是:此方法将为您提供任何类型的解析器,这不可能是真的)。您必须传递一些有T的输入,只有这样编译器才能检查它。按照惯例,可以传递一个类。但是在您的情况下,在ParserType上可能有type参数。遗憾的是,枚举和泛型不能很好地混合。@PaulBellora:没有参数。但是对于常量特定的方法和枚举方法,泛型方法定义与类关联的泛型方法没有区别。@scottb我要说的是,为了避免任何未检查的强制转换,
ParserType.APARSER
需要以某种方式提供一个
String
的泛型类型参数。换句话说,它最好是
Parser-createParser(ParserType-ParserType)
。但是枚举不能是泛型的,所以这是一个混乱的交接。这实际上是我目前正在工作的实现,我只是想应用工厂方法进行进一步的抽象,更多地用于学习目的。+1用于认识到AParser是无状态的,因此,只需要创建它的一个实例。该实例可以自由安全地共享给任何需要它的客户端代码。它绝对可以成为与枚举常量关联的实例字段。事实上,您可以删除第二个代码段。由于AParser是无状态的,因此没有任何理由创建多个实例。“在枚举中使用抽象方法定义,并将所需代码与枚举常量放在一起。”。这听起来很有趣,你能提供一个具体的例子吗,因为我又遇到了类型安全问题。事实是,如果使用枚举来表示这些解析器,你将丢失它们的泛型类型信息。@PaulBellora:当然,你是对的。我没有试过,但我只是想知道。既然无界通配符是可重定义的,那么与枚举常量的实例字段关联的解析器类型就不能是
parser
,如果是这样,是否可以在编译时在枚举方法中使用用于通过类型推断捕获通配符的助手方法捕获解析器的具体泛型类型?我只是想大声说出来。@scottb在第一部分,是的,
ParserType
可以声明一个抽象方法,返回一个
解析器,就像你的答案所建议的那样(是的,这绝对是正确的方法,而不是使用开关)。对于第二部分,不,泛型助手方法只适用于将通配符捕获作为类型参数处理,但是如果不进行未检查的强制转换,则无法从
获取类似
String
的类型参数。@ChrisGeo:我很乐意提供一个示例,但目前时间紧迫。由于您能够访问有效的Java,请转到“枚举和注释”一章。J.布洛赫举了一个例子,正好说明了我所说的。阅读这一章将使您成为丰富枚举类型的终身爱好者。
public <T> Parser<T> createParser(ParserType typ) { ... }
  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;
  }
Collection<String> it = (Collection<String>) getResults("query", APARSER);
Collection<String> it = getResults("query", new AParser());
    switch (parserType) {
        case APARSER:
            parser = new AParser();
            break;
    }