【Unity】Unityで学ぶデザインパターン03: FactoryMethod パターン【デザパタ】

# はじめに
様々な言語で「デザインパターン」の本が世の中にありますが、筆者個人の経験では
`いまいちピンとこない例` `いまいちピンとこないコード` で説明されてることが多く、
`結局これっていつ使うの？` という疑問に答えるには仕事仲間等との議論をしないと
辿り着けないことが多々ありました。

そこで特に「ゲーム開発ではどう使うか？」にフォーカスを当てて、実践的な例を交えて
デザインパターンの説明の需要があると思い記事を作りました。

## デザインパターンを学ぶ理由

デザインパターンを学ぶ理由としては
1. 車輪の再発明の防止
1. 長文で読みにくいコード（可読性の低いコード）を減らす
1. コードを疎結合にして変更に強くなる(変更時のコスト・変更箇所を減らす)
1. モジュールとして使いまわせるように、コードの再利用性を高める
といった効果を期待できます。

## 対象読者
Unity 全くの初心者(インストールしただけで触ったことがないような方)はお断りです。
最低限以下のことは理解・経験を積んでおくことが必須になります。

- MonoBehaviour 継承クラスでコードを書いたことがある
- C# のピュアクラスを用いた自作クラスを作ったことがある
- クラスの継承という概念は知っている

そのため、`脱・初心者` `中級者へのステップアップ` として `デザインパターンを学ぶ` のが良いと思います。


# デザパタ記事リンク
## 生成系
1. [AbstractFactory パターン](https://qiita.com/Cova8bitdot/items/e863cecf48c0529ba05a)
1. [Builder パターン](https://qiita.com/Cova8bitdot/items/9dcdfeb0641bc774b1f8)
1. [FactoryMethod パターン](https://qiita.com/Cova8bitdot/items/61a38fd51735f321e564)(本記事)
1. [Prototype パターン](https://qiita.com/Cova8bitdot/items/0bc251bc36201985c204)
1. [Singleton パターン](https://qiita.com/Cova8bitdot/items/29b7064c7472a6f34972)

## 構造系
1. [Adapter パターン](https://qiita.com/Cova8bitdot/items/f09776957073cf64902b)
1. [Bridge パターン](https://qiita.com/Cova8bitdot/private/5b528216952c74436045)
1. [Composite パターン](https://qiita.com/Cova8bitdot/items/1c57d856027a33e99eb0)
1. [Decorator パターン](https://qiita.com/Cova8bitdot/items/e1373a756b3dfd86c920)
1. [Facade パターン](https://qiita.com/Cova8bitdot/items/bdf0976bdfa83e57f121)
1. [Flyweight パターン](https://qiita.com/Cova8bitdot/items/7f1f1f04b46ebaf43c5e)
1. [Proxy パターン](https://qiita.com/Cova8bitdot/items/8a27137812e103ec1ee3)

## 様態・ふるまい系
1. [Chain of Responsibility パターン](https://qiita.com/Cova8bitdot/items/66c62dd96adffda7e72e)
1. [Command パターン](https://qiita.com/Cova8bitdot/items/9a88f7fc0782c729f97c)
1. [Interpreter パターン](https://qiita.com/Cova8bitdot/items/a871c1de813d14049934)
1. [Iterator パターン](https://qiita.com/Cova8bitdot/items/302f19863cc3b2235625)
1. [Mediator パターン](https://qiita.com/Cova8bitdot/items/be33b97f35c84e814df7)
1. [Memento パターン](https://qiita.com/Cova8bitdot/items/ae7e6f5d2da7b07d379b)
1. [Observer パターン](https://qiita.com/Cova8bitdot/items/632782ad5264baf6a366)
1. [State パターン](https://qiita.com/Cova8bitdot/items/ab9fc16ac3a6657fdce0)
1. [Strategy パターン](https://qiita.com/Cova8bitdot/items/28dd773075cf6e9fefee)
1. [TemplateMethod パターン](https://qiita.com/Cova8bitdot/items/f0bed164ef6563b489d0)
1. [Visitor パターン](https://qiita.com/Cova8bitdot/items/15c56d40435adf63891d)

# FactoryMethod　パターンについて
FactoryMethodパターンについてWikipedia で調べると以下のように出てきます。

```
Factory Method パターンは、他のクラスのコンストラクタを
サブクラスで上書き可能な自分のメソッドに置き換えることで、
アプリケーションに特化したオブジェクトの生成をサブクラスに追い出し、
クラスの再利用性を高めることを目的とする。
```
(https://ja.wikipedia.org/wiki/Factory_Method_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3より)

これで理解できる方は以降の文章は読まなくて良いと思います（多分その方は抽象的にものを捉える天才だと思います）。

もう少し噛み砕いた言い方をすると以下のようになります。
```
Factory の抽象クラス or インターフェースを作って、生成メソッドの実装は派生先（インターフェース実装クラス）に任せてしまおうぜ。
```

身も蓋もない話をしてしまうと、 `Factory をTemplateMethod パターンを使っていいかんじにしようぜ` ということです。


## Abstract Factory と FactoryMethodの違い

`抽象クラスを作る` という意味では AbstractFactory とFactoryMethod は `同じ` と認識して問題ありません。
ただし、両者の違いとしては`AbstractFactory は1種類以上のObject生成メソッド(API)を1つ以上持っている` と `FactoryMethod はコンストラクタ相当のメソッド(API)しか持たない` というところでしょう。

少し具体的な例を考えてみましょう。
ここでは [Builderパターン]() でも使った「スケルトンの敵」を用意することを考えてみましょう。

### Abstract Factory とBuilder のおさらい
```SkeletonEnemy.cs

public abstract class BaseSkeletonEnemyFactory 
{
	public Enemy Create(){return new Skeleton();}
	public abstract Weapon CreateWeapon();
	public abstract Armor CreateArmor();
}

public class NormalSkeletonFactory : BaseSkeletonEnemyFactory
{
	// 素のスケルトンは裸一貫
	public override Weapon CreateWeapon(){return null;}
	public override Armor CreateArmor(){return null;}
}

public class EliteWarriorSkeletonFactory : BaseSkeletonEnemyFactory
{

	// Elite は銅の剣を持っている
	public override Weapon CreateWeapon(){return new BronzeSword();}

	// Elite は革防具を持つ
	public override Armor CreateArmor(){return new LeatherArmor();}
}

public class SkeletonBuilder
{
	BaseSkeletonEnemyFactory _factory = null;
	public SkeletonBuilder(BaseSkeletonEnemyFactory myfactory)
	{
		_factory = myfactory;
	}

	public Enemy Build()
	{
		var e = _factory.Create();
		var weapon = _factory.CreateWeapon();
		if( weapon != null )
		{
			e.SetWeapon(weapon);
		}
		var armor = _factory.CreateArmor();
		if( armor != null )
		{
			e.SetArmor(armor);
		}

		return e;
	}
}


public static class GameLogic
{
	public static void Main()
	{
		// ステージ1 用の設定
		SkeletonBuilder[] builders = CreateLevel1EnemyBuilders();
		foreach(var b in builders)
		{
			b.Build();
		}


		// ステージ2 用の設定
		builders = CreateLevel2EnemyBuilders();
		foreach(var b in builders)
		{
			b.Build();
		}

	}

	public static SkeletonBuilder[] CreateLevel1EnemyBuilders()
	{
		// Level1 は雑魚スケルトン3体
		return new SkeletonBuilder[]{
			SkeletonBuilder( new NormalSkeletonFactory() );
			SkeletonBuilder( new NormalSkeletonFactory() );
			SkeletonBuilder( new NormalSkeletonFactory() );
		}
	}


	public static SkeletonBuilder[] CreateLevel2EnemyBuilders()
	{
		// Level2 は雑魚スケルトン2体 + エリートスケルトン1体
		return new SkeletonBuilder[]{
			SkeletonBuilder( new NormalSkeletonFactory() );
			SkeletonBuilder( new EliteWarriorSkeletonFactory() );
			SkeletonBuilder( new NormalSkeletonFactory() );
		}
	}
}
```

上記では `BaseSkeletonEnemyFactory` がAbstractFactory、SkeletonBuilderが Builderパターンで実装されていて、ゲームロジック側でそれぞれのLevelに合わせて敵の生成を行なっています。

BaseSkeletonEnemyFactory では `スケルトン種族の敵` が必要な要素を全て提供する機能を有しています。
具体的には「素体のスケルトン」「装備させる武器」「装備させる防具」を提供するAPIがあります。

そしてBuilder はこれらのAPIを呼んで、必要に応じて装備させて最終的な敵スケルトンを作ってくれます。
Level1 では雑魚スケルトンが3体出てきて、Level2 では雑魚2体 + エリート1体という構成にして、実体の敵はBuilderを介して取得するようにしています。

### 敵をふやしたときの話

しかし、ゲームを作るとなると、もう少し敵を増やしたいところです。
ここではLevel2 をもう少し難しくするため「スライムの敵」を追加してみましょう。

```EnemySlimeFactory.cs
public abstract class BaseSlimeEnemyFactory 
{
	public Enemy Create(){return new Slime();}
	public abstract Color GetColor();
	public abstract Vector3 GetSize();
}

public class GreenSlime : BaseSlimeEnemyFactory
{
	public override Color GetColor(){return new Color32(0,255,0,255);}
	public override Vector3 GetSize();{return Vector3.one;}
}
```

このような形で緑スライムを生成する工場を作ってみます。

ここでふとコードを見比べると、BaseSkeletonEnemyFactory もBaseSlimeEnemyFactory も
`public Enemy Create()` というメソッドで素体を生成しています。

これを抽象化すると `「全ての敵は素体が必要で、それはCreate() で作る」` というふうに言い換える事ができます。
そこで以下のような1段階抽象的なEnemyFactoryを考えてみます。

```BaseEnemyFactory.cs
public abstract class BaseEnemyFactory
{
	public abstract Enemy Create();
}
```

このようにするとBaseSkeletonEnemyFactory とBaseSlimeEnemyFactory は以下のように書き換えられます。
```NewEnemyFactory.cs

public abstract class BaseSkeletonEnemyFactory : BaseEnemyFactory
{
	public override Enemy Create(){return new Skeleton();}
	public abstract Weapon CreateWeapon();
	public abstract Armor CreateArmor();
}

public abstract class BaseSlimeEnemyFactory 
{
	public override Enemy Create(){return new Slime();}
	public abstract Color GetColor();
	public abstract Vector3 GetSize();
}

```

このようにすると、`全ての敵はCreate() で作成可能`, だが `どんな敵を生成するかは派生先に任せる` ことができます。これがFactory Method のやり方です。

## FactoryMethod のメリット・デメリット

FactoryMethod はTemplateMethod パターンの利用例の一つです。

メリットとしては以下のことが挙げられます
- 抽象化してコードを書ける
- - 実装先(具象クラス) への依存度を下げる
- - コードを使う側が「どのクラスを使うべきか」という思考を減らす事ができる
- 実装漏れを防ぎやすい


### 抽象化による恩恵
ここは難易度がやや高めな話ではありますが、抽象化レベルをあげると「具象クラスへの依存度を減らす」ことと「コードを利用する側の負担を減らす」ことができます。
前者について、例えばGameLogic 部分で以下のように直接具象クラスを参照すると、以下のようなコードになります。

```BadDependencyLogic.cs


public static class GameLogic
{
	public static void Main()
	{
		// ステージ1 用の設定
		Enenmy[] enemyies = new Enenmy[]{
            new NormalEnemySkeleton(),
            new NormalEnemySkeleton(),
            new NormalEnemySkeleton()
        };
        foreach(var e in enemyies)
        {
            e.SetUp();
        }

		// ステージ2 用の設定
		enemyies = new Enenmy[]{
            new NormalEnemySkeleton(),
            new EliteEnemySkeleton(),
            new NormalEnemySkeleton()
        };
        foreach(var e in enemyies)
        {
            e.SetUp();
        }
	}
}
```

このようにすると、Enemyの変更ごとにMain のロジックを修正しなくてはなりません。
そこで、ロジックとパラメータを分離する事で「既存ロジックに手を加えることなく、コードを修正する」ことができます。
より具体的に言えば `Main()では今後、「敵の種族追加」や「登場的の変更」を行なってもコードを修正する必要がない` ということになります。
このようにすることで保守性を高めたり、ロジックのコードの可読性を上げることにつながります。

```BetterDependencies.cs
public static class GameLogic
{
	public static void Main()
	{
		// ステージ1 用の設定
		SkeletonBuilder[] builders = CreateLevel1EnemyBuilders();

        // foreach内部ではBuilder しかみていないため、どんな敵が登場するのかなど考える必要がない
		foreach(var b in builders)
		{
			b.Build();
		}

		// ステージ2 用の設定
		builders = CreateLevel2EnemyBuilders();
		foreach(var b in builders)
		{
			b.Build();
		}
	}

	public static SkeletonBuilder[] CreateLevel1EnemyBuilders()
	{
		// Level1 は雑魚スケルトン3体
		return new SkeletonBuilder[]{
			SkeletonBuilder( new NormalSkeletonFactory() );
			SkeletonBuilder( new NormalSkeletonFactory() );
			SkeletonBuilder( new NormalSkeletonFactory() );
		}
	}


	public static SkeletonBuilder[] CreateLevel2EnemyBuilders()
	{
		// Level2 は雑魚スケルトン2体 + エリートスケルトン1体
		return new SkeletonBuilder[]{
			SkeletonBuilder( new NormalSkeletonFactory() );
			SkeletonBuilder( new EliteWarriorSkeletonFactory() );
			SkeletonBuilder( new NormalSkeletonFactory() );
		}
	}
}
```


### 実装漏れの防止
これはTemplateMethod の特性というかC#の抽象クラスの特性にもなります。
抽象クラスでいえば `abstract` キーワードで指定されたメソッドは`派生先での実装をしないとコンパイルエラー` になります。
また、Interface も同様に、`インターフェースのメソッドを定義しないとコンパイルエラー` になります。

このようにしておけば、派生先のクラスで「実装漏れによる不具合」を圧倒的に減らす事が可能です。


# まとめ

FactoryMethod はTemplateMethod の一つです。
特にAbstract Factory をさらに抽象化するときに使ったり、抽象クラスではなく「インターフェースによるメソッド定義」も利用する事ができます。

種類が多くなりそうな機能の実装などに特に効果的で、処理をシンプルにすることに非常に役立つデザインパターンです。


# 参考
TechScore さん: https://www.techscore.com/tech/DesignPattern/FactoryMethod
Wikipedia: https://ja.wikipedia.org/wiki/Factory_Method_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3
実践Python3： https://www.oreilly.co.jp/books/9784873117393/