简体   繁体   English

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

[英]Strategy Design Pattern, Generics and TypeSafety

I want to create the following Strategy Pattern combined with Factory, but I want it to be typesafe. 我想创建以下策略模式与Factory结合,但我希望它是类型安全的。 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: 检查有效的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();

}

So now I can use as Ingo suggested 所以现在我可以使用Ingo建议

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. 我只是将一个Parser<T>传递给getResults()方法并忘记那个工厂的东西。 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. 此外,您无法从该函数返回Parser<String> ,因为String与调用者想要的任何类型都不相同。

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. 你有你想要的: getResult方法独立于解析器的工作方式,但它返回一个正确类型的集合。

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: 如果每次都想要一个新实例,可以传递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
    }

}

Note: I'm eager to find a better approach so if you have some thoughts please share them in a comment. 注意:我很想找到更好的方法,所以如果您有一些想法,请在评论中分享。

I applaud your desire to use the Strategy Pattern; 我赞赏你使用战略模式的愿望; +1 for that. 为此+1。 I do think Ingo's comments are spot on. 我确实认为Ingo的评论是现货。

Just an additional comment (taken from Effective Java, 2nd Ed.): 只是一个额外的评论(取自Effective Java,2nd Ed。):

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

To use Joshua Bloch's words, "this solution appears compact and even elegant." 用约书亚布洛赫的话来说,“这个解决方案显得紧凑而且优雅。” However it may also be fragile and difficult to maintain. 然而,它也可能是脆弱的并且难以维护。 In general, you should try not to switch on enum constants because whenever you do so, your code will break whenever you change the enum. 通常,您应该尝试不要打开枚举常量,因为每当您这样做时,只要更改枚举,代码就会中断。

This is exactly the right time to use an abstract method definition in your enum and place the desired code right there with the enum constant . 这正是在枚举中使用抽象方法定义的正确时间,并使用枚举常量将所需代码放在那里 Doing this guarantees that you never forget to add the needed enum associated code to your project and assures that whenever you add to your enum that your project won't break. 这样做可以保证您永远不会忘记将所需的枚举关联代码添加到项目中,并确保每次添加到枚举时项目都不会中断。

In fact, if your goals allow for it, it may even be possible or adviseable to move the entire factory method to your enum and have your enum class implement the Strategy interface. 事实上,如果你的目标允许,甚至可能或建议将整个工厂方法移动到你的枚举,让你的枚举类实现Strategy接口。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM