Unityで学ぶCompositeパターン ~ Viewコンポーネントを複数にする(追記) ~

[前の記事](https://qiita.com/nebusokuhibari/items/356def5695aca4a2b8c5)　－　[目次](https://qiita.com/nebusokuhibari/items/f403b40bea0ad790176c)　－　[次の記事](https://qiita.com/Nakashima_Toshiki/items/a3526fb82e590af4f514)

[Unityで学ぶMVPパターン(2) ~ Viewコンポーネントを複数にする ~](https://qiita.com/NakashimaToshiki/items/f2c3403859422f5c0e20)
でCompositeパターンを使ったほうが良いと思ったので、記事の追記・修正です。

## 目的

MVPパターンでViewコンポーネントを複数対応化のため、
Presenterのview変数を配列化する必要があるという欠点があった。

Compositeパターンでこの問題を解決したい。

## シーンの用意

シーンは前の使いまわしです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/291470/0d655051-923e-b389-ed0d-e2d946da2273.png)

Githubにプロジェクトを置いておきます。
[04-PropertyChanged-HealthBar](https://github.com/NakashimaToshiki/Adaptive-Code-Unity/tree/master/04-PropertyChanged-HealthBar)

## コンポーネントの作成

Presenterを修正してCompositeコンポーネントを新たに作成します。

### ParameterPresenter

配列型だったview変数を単体の変数に戻します。
この変数に後述で作成するCompositeコンポーネントを登録します。

```C#:ParameterPresenter.cs
using UnityEngine;
using System.ComponentModel;

public class ParameterPresenter : MonoBehaviour {

    public StatusModel model;

    public ParameterViewBase healthView;

    void Awake()
    {
        model.PropertyChanged += Model_PropertyChanged;
        Model_PropertyChanged(model, new PropertyChangedEventArgs("")); // 開始時に値を発生させる用
    }

    private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        healthView.SetParameter(model.HealthMax, model.Health);
    }
}
```

### ParameterViewComposite

こちらがCompositeパターンを実装するコンポーネントです。

ParameterViewBaseを継承して、且つParameterViewBaseのコレクションを所持していることがミソです。
このパターンは再帰的な処理をする役割を持っており、
SetParameterメソッドは、リストにある全てのSetParameterも呼び出す仕組みになっています。

つまり、これでPresenter側はCompositeコンポーネントからSetParameterを呼び出せば、
複数のViewを更新することができます。

当然ですが、PresenterはView単体でも登録可能なので、適応性がグンッと上がります。


ついでに、AddComponentInChildrenメソッドを作成して、このコンポーネントがあるゲームオブジェクトの子からParameterViewBaseコンポーネントを継承したコンポーネントを検索して、childrenリストに追加するプログラムを作成します。

GUIのルートオブジェクト（Canvasオブジェクト）に、このコンポーネントを追加しておいて、
、コンポーネント上のコンテキストメニューから実行すれば、リスト生成の手間が省けます。
（ZenjectとかのDIコンテナフレームワークにある自動で依存性を注入する機能に似ている？）

```C#:ParameterViewComposite.cs
using System.Collections.Generic;
using UnityEngine;
using System.Linq;

public class ParameterViewComposite : ParameterViewBase
{
    public List<ParameterViewBase> children;

    [ContextMenu("AddComponentInChildren")]
    public void AddComponentInChildren()
    {
        gameObject.GetComponentsInChildren<ParameterViewBase>(children);
        children.Remove(this);
    }

    public override void SetParameter(int max, int value)
    {
        children.ToList().ForEach(child => child.SetParameter(max, value));
    }
}
```

## 完成

結果は前回と変わらず、inspectorからHealthの値を変えると

- 体力バーの長さが変わる
- テキスト表示が更新される
- 画面上下が赤くなる

で３種類のViewが反映されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/291470/aa9b35bd-4e33-da72-d0ed-61df4dcbef4b.png)

Githubに完成プロジェクトを上げておきます。

[07 Composite-HealthBar](https://github.com/NakashimaToshiki/Adaptive-Code-Unity/tree/master/07%20Composite-HealthBar)

## まとめ

MVPパターンでViewコンポーネントを複数対応化の際に
Presenterのview変数を配列化する修正が必要だったのが、
これで解決しました。

Compositeパターンのリストに
登録しているViewをアクティブにして、
登録していないViewは非アクティブにすれば、
動作しているGUIのみ表示するといったことができそう。

それを実現する良さげなパターンが見つかったら、また記事にしたいです。