プログラムを複雑にする「場合分け」のコード
区分ごとのクラスのインスタンスを生成する
場合分けのロジックを整理する
if/ else を使った処理を場合分けするコードは、ロジックが複雑になり、可読性が低くなりがちである。
そうならないために、有効なのがポリモーフィズム(多態性)の利用である。
例えば、以下の様な例を想定する
- ポケモンへの指示を送る。
- そのポケモンが炎タイプなら、火のこが発動する
- そのポケモンが水タイプなら、水てっぽうが発動する
- そのポケモンが草タイプなら、はっぱカッターが発動する
public class Main {
public static void main(String[] args) {
Pokemon pokemon = new Pokemon(Hitokage);
pokemon.attack();
}
}
public class Pokemon {
public void attack() {
if(isHitokage()) {
System.out.println("火の粉");
} else if(isZenigame()) {
System.out.println("水てっぽう");
} else if(isFushigidane()) {
System.out.println("はっぱカッター");
}
}
}
見ての通り、if/else を使ったことで、大変読みにくくなってしまった。
しかし、ポリモーフィズムを取り入れることで、以下のようになる。
public class Main {
public static void main(String[] args) {
Pokemon pokemon = new Hitokage();
pokemon.attack();
}
}
public interface Pokemon {
public void attack();
}
public class Hitokage implements Pokemon {
public void attack() {
System.out.println("火の粉");
}
}
public class Zenigame implements Pokemon {
public void attack() {
System.out.println("水てっぽう");
}
}
public class Fushigidane implements Pokemon {
public void attack() {
System.out.println("はっぱカッター");
}
}
場合分けによるインスタンスの生成ロジックを整理する
コードから if/ else がなくなり、attack()メソッドがシンプルになった。
次に、Hitokage, Zenigame, Fushigidane インスタンスの生成について、以下の様な場合を考えてみる。
- インスタンスを生成する。
- 引数が FireType なら、Hitokage インスタンスを生成する。
- 引数が WaterType なら、Zenigame インスタンスを生成する。
- 引数が GrassType なら、Fushigidane インスタンスを生成する。
public class Main {
public static void main(String[] args) {
Pokemon pokemon = OkidoHakase.presentPokemon("FireType");
pokemon.attack();
}
}
public class OkidoHakase {
public static Pokemon presentPokemon(String type) {
if(type.equals("FireType")) {
return new Hitokage();
} else if(type.equals("WaterType")) {
return new Zenigame();
} else if(type.equals("GrassType")) {
return new Fushigidane();
}
}
}
やはり、インスタンスの生成ロジックにどうしても、if/else を使うことになってしまう。
しかし、ちょっとした工夫でこの if/else をなくすことができる。
public class Main {
public static void main(String[] args) {
Pokemon pokemon = OkidoHakase.presentPokemon("FireType");
pokemon.attack();
}
}
public class OkidoHakase {
static Map<String, Pokemon> pokemonLineUp;
static {
pokemonLineUp = new HashMap<>();
pokemonLineUp.put("FireType", new Hitokage());
pokemonLineUp.put("WaterType", new Zenigame());
pokemonLineUp.put("GrassType", new Fushigidane());
}
public static Pokemon presentPokemon(String type) {
return pokemonLineUp.get(type);
}
}
上記のように、Map の中にキーとインスタンスを紐づけて入れておき、引数のキーを利用してインスタンスを取得するメソッドを用意しておくことで、インスタンスの生成においても if/ else 使用しなくても済むようになる。
なお、上記例において、static ブロックを使用しているのは
- そもそも、presentPokemon メソッドが static メソッドであるため、コンストラクタによる初期化では遅い。
- pokemonLineUp 変数の定義に static ブロック内の処理を入れ込むことも可能だが、それでは、変数の定義という責務を超えた処理となり、可読性が落ちる。
という理由からである。 - この設計手法をファクトリーパターンという。