LoginSignup
0
0

ざっくりコンポジション【Unity/C#】

Last updated at Posted at 2023-12-14

はじめに

Applibot Advent Calendar 2023」14日目の記事になります。
前回の記事はコチラです。

今回は、どういう時にコンポジションで持った方がいいんだっけというのを改めて考える機会があったので自分なりにまとめてみました。割と継承とコンポジションで比較されることがありますが、そんなときにコンポジションを選択するべきときについて記載しています。
例として Unity/C# でのコードを記載しています。

コンポジションとは

委譲、合成とも呼びます。厳密には使い分けがあるっぽい?です。
ざっくりコードで書くと

public class Enemy
{
    private float _attackPower;
    private float _hp;

    public Enemy(float attackPower, float hp)
    {
        _attackPower = attackPower;
        _hp = hp;
    }
}

public class Enemy
{
    private Status _status;

    public Enemy(Status status)
    {
        _status = status;
    }
}

public class Status
{
    public Status(float attackPower, float hp)
    {
        AttackPower = attackPower;
        Hp = hp;
    }

    public float AttackPower { get; }
    public float Hp { get; }
}

こうなるイメージです。
Statusは明確にEnemyと役割が違うので分けた方がわかりやすいよね、という感じです。
当たり前と言えば当たり前ですが、クラス分けをする際に切り分ける指標として役割ベースで分けていくと考えやすいと思いました。正直ここではメリットが少ないですが、他にメリットがあるので紹介していきます。
コンポジションで持つメリットとしては、

  • 必要な要素だけを公開することができる
  • 名前空間、クラスを分けることで責任の所在・役割を明確に分けることができる
  • 変更に強い

この3つがあると思っています。
この例として、UnityUI の表示をCanvasGroupで操作するコンポーネントについて考えてみます。

メリット1:必要な要素だけを公開することができる

CanvasGroupのアルファを変える時に .alpha でアルファをセットできるがここを数値で指定ではなく、表示と非表示のみの機能に制限したいとします。その場合CanvasGroupに拡張メソッドを生やしても、継承したとしても(そもそも Unity のCanvasGroupsealedなので継承できないが)alpha にはアクセスできてしまいます。
そこでコンポジションとしてCanvasGroupを持った別クラスを用意することで、.alphaへのアクセスを制限することができ、.alphaの値は絶対SetViewActiveを通してでしか変更できないようにできます。

public class UIVisibilityController : MonoBehaviour
{
    [SerializeField] private CanvasGroup _canvasGroup;

    public void SetViewActive(bool isActive)
    {
        _canvasGroup.alpha = isActive ? 1f : 0f;
    }
}

メリット2:名前空間、クラスを分けることで責任の所在・役割を明確に分けることができる

さらにこれを独自のnamespaceに入れておくことで、Unity 側の機能であるCanvasGroupと自分の書いたコードで責任を明確に分けることができ、バグが起きた時に原因の特定がしやすくなります。

メリット3:変更に強い

そして、そもそも表示非表示の切り替えをCanvasGroup以外のもので管理したいとなった場合にはコンポジションで持つクラスとSetViewActiveの中の実装を変えるだけで済むため、変更に強いということになります。

おまけ

実際CanvasGroupをラップして持つなら、Unity のCanvasGroup側を Inspector からいじられたくないのでGUI表示をいじらないようにするべきだな、と書いてて思いました。
コード書くならこんな感じのイメージです。

UIVisibilityController.cs
using UnityEngine;

[RequireComponent(typeof(CanvasGroup))]
public class UIVisibilityController : MonoBehaviour
{
    [SerializeField] private CanvasGroup _canvasGroup;
        
    public void SetViewActive(bool isActive)
    {
        _canvasGroup.alpha = isActive ? 1f : 0f;
    }

    // エディタでアタッチ時にコンポーネントを取得
    private void Reset()
    {
        _canvasGroup = GetComponent<CanvasGroup>();
    }
}
CanvasGroupEditor.cs
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(CanvasGroup))]    
public class CanvasGroupEditor : Editor
{
    public override void OnInspectorGUI()
    {
        // 表示自体消してしまうとエディタでのデバッグがしにくくなるためdisableで表示
        EditorGUI.BeginDisabledGroup(true);
		// デフォルトのCanvasGroupのInspectorを表示
        base.OnInspectorGUI();
    }
}

image.png

終わりに

Unity/C# を例にコンポジションの採用基準・メリットについて紹介しました。もし上記のメリットに利を感じたときはコンポジションとして持つことを検討してみてください。

以上、「Applibot Advent Calendar 2023」14日目の記事でした!
明日は、@nakashii_さんの記事です!よろしくお願いします!

最後まで読んでいただきありがとうございました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0