きり丸の技術日記

技術検証したり、資格等をここに残していきます。

Javaでストラテジーパターンを素振りする

ストラテジーパターンというGoFのデザインパターンが良いコード悪いコードでも紹介されていたので、素振りします。

自分の言葉で上手な言語化ができていないので、メリットは参考情報や良いコード悪いコードの購入、または別の方の記事を参考にしてください。

環境

  • Java
    • 17

今回の処理

複数の言語の文章を、ひとつの言語に変換する。

※ 変換の具体的なロジックは実装しない。

対応

文章と言語名が記載されたDTOを生成する。

@Data
public class Question {
  private String sentence;
  private Locale locale;
}

ストラテジーパターンを使用しやすいように、FCCを使用してQuestionのリストを用意し、LocaleをKeyとしたMap型に変換する。

@Data
@Builder
public class QuestionList {
  List<Question> value;
  public Map<Locale, List<Question>> groupByLocale() {
    return value.stream()
        .collect(Collectors.groupingBy(
            Question::getLocale
        ));
  }
}

翻訳用のinterfaceを用意する。

public interface Translator {
  List<Question> translate(List<Question> question);
}

interfaceを継承した翻訳クラスを用意する。

public class FromEnToJa implements Translator {

  @Override
  public List<Question> translate(List<Question> question) {
    return question.stream()
        .map(e -> e.toBuilder().locale(Locale.JAPANESE).build()
        ).collect(Collectors.toList());
  }
}

public class FromFrToJa implements Translator {

  @Override
  public List<Question> translate(List<Question> question) {
    return question.stream()
        .map(e -> e.toBuilder().locale(Locale.JAPANESE).build()
        ).collect(Collectors.toList());
  }
}

public class NoopTransrate implements Translator {

  @Override
  public List<Question> translate(List<Question> question) {
    return question;
  }
}

LocaleをKeyとし、実際に処理するクラスをvalueとしたMapを用意する。

private final Map<Locale, Translator> toJaActionMap =
    Map.of(
        Locale.ENGLISH, new FromEnToJa(),
        Locale.JAPANESE, new NoopTransrate(),
        Locale.FRENCH, new FromFrToJa()
    );

文章とLocaleを元に、すべてを日本語に変換する処理をする。

public QuestionList translate() {
  final List<Question> list = new ArrayList<>();
  // 処理を移譲しているので、中身が違う翻訳処理を行う。
  this.groupByLocale().forEach(
      (key, value) -> list.addAll(toJaActionMap.get(key).translate(value))
  );
  return new QuestionList(list);
}

ソースコード

終わりに

正直、今回の例示とした委譲処理はメリット薄いです。ただ、ストラテジーパターンを素振りしておくことで、見通しの悪い処理を切り出せるという意識を持っておきたいです。

地味ながら、JavaGoldで勉強したBiConsumerを使うタイミングが分かっていなかったので、今回のストラテジーパターンを素振りすることで使用できてよかったです。

// ここが内部的にはBiConsumerで処理されています。
this.groupByLocale().forEach(
  (key, value) -> list.addAll(toJaActionMap.get(key).translate(value))
);

参考情報