Unity公式が公開しているプログラミングパターンを読み取ってみた！～その1. SingleResponsibility(単一責任の原則)～

# はじめに
Unity公式がプログラミングパターンのデモを説明書と共に全12種公開していたので、1つずつ読み取っていきたいと思います！
英語の説明書でしたので間違った解釈をしているかもしれませんが、ご了承下さい！
最初の5種は、SOLID原則に沿ったデモとなっており、コードのみが公開されています。
残り7種は、Scene付きのデモが入っていますので、ZIPファイルをダウンロードして試してみてください。
今回は、1つ目のSingleResponsibility(単一責任の原則)についてです。

https://github.com/Unity-Technologies/game-programming-patterns-demo

https://resources.unity.com/games/level-up-your-code-with-game-programming-patterns

# 環境
OS : Windows10
Unity バージョン : 2021.3.8f1

# SingleResponsibility(単一責任の原則)とは
SingleResponsibility(単一責任の原則)は、各モジュール、クラス、関数が 1 つのことに責任を持ち、ロジックのその部分のみをカプセル化することを述べています。
要するに、各モジュール、クラス、関数は様々な機能を実装せずに、1つの機能だけを実装するということです。
一見、簡単なように思えますが、現場で実際のコードを見ていると1つのクラスが多くの機能を持ち、神クラスになっているのを目にすることがあります。
掲載されていたコードには悪い例として以下のようなコードが掲載されていました。

### 悪いコード例
```c#
public class UnrefactoredPlayer : MonoBehaviour
    {
        [SerializeField] private string _inputAxisName;
        [SerializeField] private float _positionMultiplier;
        private float _yPosition;
        private AudioSource _bounceSfx;

        private void Start()
        {
            _bounceSfx = GetComponent<AudioSource>();
        }

        private void Update()
        {
            float delta = Input.GetAxis(_inputAxisName) * Time.deltaTime;

            _yPosition = Mathf.Clamp(_yPosition + delta, -1, 1);

            transform.position = new Vector3(transform.position.x, _yPosition * _positionMultiplier, transform.position.z);
        }

        private void OnTriggerEnter(Collider other)
        {
            _bounceSfx.Play();
        }
    }
```
このクラスは、以下3つの機能を1つのクラスで実装してしまっています。
- サウンド
- ユーザーの入力
- プレイヤーの移動

現時点では比較的短いクラスですが、プロジェクトが進むにつれて読み取ることが難しいクラスになっていきます。
単一責任の原則を守って書くと以下のようになります。

### 良いコード例
```C#
[RequireComponent(typeof(PlayerAudio), typeof(PlayerInput), typeof(PlayerMovement))]
    public class Player : MonoBehaviour
    {
        [SerializeField] private PlayerAudio playerAudio;
        [SerializeField] private PlayerInput playerInput;
        [SerializeField] private PlayerMovement playerMovement;

        private void Start()
        {
            playerAudio = GetComponent<PlayerAudio>();
            playerInput = GetComponent<PlayerInput>();
            playerMovement = GetComponent<PlayerMovement>();
        }
    }
```
```C#
public class PlayerAudio : MonoBehaviour
    {
        private AudioSource bounceSfx;

        private void Start()
        {
            bounceSfx = GetComponent<AudioSource>();
        }

        private void OnTriggerEnter(Collider other)
        {
            bounceSfx.Play();
        }
    }
```
```C#
public class PlayerInput : MonoBehaviour
    {
        [SerializeField] private string inputAxisName;

        private void Update()
        {
            float delta = Input.GetAxis(inputAxisName) * Time.deltaTime;
        }
    }
```
```C#
public class PlayerMovement : MonoBehaviour
    {
        [SerializeField] private float positionMultiplier;
        private float yPosition;

        private void Update()
        {
            transform.position = new Vector3(transform.position.x, yPosition * positionMultiplier, transform.position.z);
        }
    }
```
Playerクラスは、3機能を管理するクラスで、他3クラスは各々1つの機能のみ実装しています。
こうすることで、プロジェクトが進行していってもコードの修正が簡単になります。

# 単一責任の原則を守るために気を付けておくこと
単一責任の原則を守るためには、以下3点を念頭に置いておくと良いらしいです。
- 可読性
厳格なルールはありませんが、多くの開発者は200 ~ 300行の制限を設定しています。チームとして、「短い」とは何かを決め、それを超えた場合は、より小さな部品にリファクタリングできるかどうか判断してください。
- 拡張性
小さなクラスは簡単に継承でき拡張性が高くなります。意図しない機能を壊すことなく、変更または交換できるよう心掛けましょう。
- 再利用性
クラスを小さくモジュール化して設計し、他の部分で再利用できるようにします。

リファクタリングする際は、コードを置き換えることでチームの生産性がどのように向上するか十分に検討してくださいとのことです。検討には時間がかかりますが、長期的なコスト削減という見返りがある場合が多いとのことです。

# 参考

https://blog.unity.com/ja/games/level-up-your-code-with-game-programming-patterns