8

Factoryと組み合わせた以下のStrategy Patternを作りたいのですが、typesafeにしたいです。私は今まで次のことをしました:

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

Parser<T>メソッドに aを渡すだけ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, 2nd Ed. から引用):

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

Joshua Bloch の言葉を借りれば、「このソリューションはコンパクトでエレガントにさえ見えます」。ただし、壊れやすく、維持するのが難しい場合もあります。一般に、列挙型定数をオンにしないようにする必要があります。これをオンにすると、列挙型を変更するたびにコードが壊れてしまうからです。

これは、列挙型で抽象メソッド定義を使用し、列挙型定数を使用して目的のコードをそこに配置するのに最適なタイミングです。これを行うと、必要な列挙型関連コードをプロジェクトに追加することを決して忘れないことが保証され、列挙型に追加するたびにプロジェクトが壊れないことが保証されます。

実際、目標が許せば、ファクトリ メソッド全体を enum に移動し、enum クラスに Strategy インターフェースを実装させることも可能または推奨される場合があります。

于 2013-11-11T18:12:25.343 に答える