社内勉強会 - 4 迷宮化した分岐処理をときほぐす1

# 1. はじめに
会社で第4回の勉強会を主催したので学習内容のメモとなります。
条件分岐の解きほぐし方に関する勉強会を開きました。

 # 2. 概要
今回のテーマは、**条件分岐の解きほぐし方**です。
今回の記事は、RPGを例にストラテジーパターンについて説明しています。

※解釈がずれている箇所がありましたら教えていただければと思います。
 参考資料[1]：「良いコード/悪いコードで学ぶ設計入門 ―保守しやすい 成長し続けるコードの書き方」


# 3. 場面設定
勇者は3種類の魔法を操り敵に攻撃します。
![Strategy黎.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691844/0a7d500c-4a6e-3614-989f-f59bfef52cd9.png)

## 3.1. 問題
呪文選択の処理を普通に書いてみる（if/elseで書いても大体おなじ）
この段階で考えられる問題点
1. Magicクラスの関心事は呪文が増えるたびに増加していく
2. データやロジックの関係性が複雑化 nameを共有しているとか

```java
enum MagicType {
	fire,
	burizado,
	sanda
}

// Magic magic = new Magic(MagicType.fire);
public class Magic {
	final String name;
	Magic(MagicType magicType) {
		switch(magicType) {
			case fire:
				this.name = "ファイア";
				break;
			case sanda:
				this.name = "サンダー";
				break;
			case burizado:
				this.name = "ブリザド";
				break;
			default:
				this.name = "";
				break;
		}

	}
}
```

では、勇者のほかにロビンフッドがいたとします。
ロビンフッドが使える魔法はブリザドだけだとした時に次のような問題があります。
1. ロビンフッドにはファイアとサンダーの処理を意識させたくない（関心事を分けたい）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691844/cab5330e-0edc-c750-c203-4e765db008b0.png)

```java
switch(magicType) {
/* ロビンフッドには不要な関心事
    case fire:
        this.name = "ファイア";
        break;
    case sanda:
        this.name = "サンダー";
        break;
*/
    case burizado:
        this.name = "ブリザド";
        break;
    default:
        this.name = "";
        break;
}
```

# 4 ストラテジーパターンで改善
それでは条件分岐をストラテジーパターンで解きほぐしていきます。

## 4.1 魔法の処理をクラスに分ける

```java
public class Burizado {

	private final Member member;

	Burizado(Member member) {
		this.member = member;
	}
 
	public String name() {
		return "ブリザド";
	}

	public MagicPoint cost() {
		return new MagicPoint(3);
	}
}

```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691844/54f49002-9b14-5f7c-2399-85ac2667e227.png)

## 4.2 インターフェースを作る
メリット
- 各魔法の処理がどこにあるかがわかりやすい
- Fireに変更を加えてもBurizadoには影響なし
    - switchだとインスタンス変数の変更影響など考えないといけないかも

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691844/0caa299e-8998-2e8d-58ca-9a89fdd1fb8f.png)


```java
public interface Magic {
	public String name();
	public MagicPoint cost();
}

Magic fire = new Fire(member);
Magic brizado = new Burizado(member);
```

## 4.3 必要のない魔法の関心事をなくす
Mapを使って実装してみる。
Magic Interfaceで共通化してることにより、1役者１つのMapで魔法を管理することが可能。
```java
Map<MagicType, Magic> yuusya = new HashMap<MagicType, Magic>();
Map<MagicType, Magic> robinhood = new HashMap<MagicType, Magic>();

Magic fire = new Fire(member);
Magic brizado = new Burizado(member);

yuusya.put(MagicType.fire, fire);
yuusya.put(MagicType.burizado, brizado);

robinhood.put(MagicType.burizado, brizado);
System.out.println(yuusya.get(MagicType.fire).name()); 			// ファイア
System.out.println(yuusya.get(MagicType.burizado).name());		// ブリザド
System.out.println(robinhood.get(MagicType.burizado).name());	// ブリザド
System.out.println(robinhood.get(MagicType.fire).name());		// エラー
```

メリット
- robinhoodの関心事を制限できている
- 魔法使用の拡張性もしやすい

```java
robinhood.put(MagicType.burizado, brizado);
System.out.println(robinhood.get(MagicType.burizado).name());	// ブリザド
System.out.println(robinhood.get(MagicType.fire).name());		// エラー

// 魔法を追加したかったらMapに対してkey/valueを追加してあげる
robinhood.put(MagicType.fire, fire);
robinhood.put(MagicType.sanda, sanda);
System.out.println(robinhood.get(MagicType.fire).name());		// ファイアー
System.out.println(robinhood.get(MagicType.sanda).name());		// サンダー
```


# 5. まとめ

今回のゴール：条件分岐をスマートに書く方法を知る。
条件分岐の役割をクラスそのものに任せる。

インターフェースを使って関心事の分離を行う
→　条件分岐を解きほぐすこともできる




# 6. 参考文献
1. 良いコード/悪いコードで学ぶ設計入門 ―保守しやすい 成長し続けるコードの書き方