備忘録の箱

日常で気になったことを備忘録として残していきます

partialを用いたクラス設計

業務でpartialを使うことがあったのでメモ

そもそもpartialって何?

結論から言うと、partialはクラスの定義を複数のファイルに分割する時に使うらしい

例えばUnityでクラスを定義する時は、ソースファイル名に基づいて同名のクラスを
定義するがpartialを使うことで複数のソースファイルに同じクラスを定義できるとのこと

 

サンプルソース

SampleClass.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public partial class SampleClass : MonoBehaviour {

	private int aaa = 10;

	// Use this for initialization
	void Start () {
		this.Func();
	}
	
	// Update is called once per frame
	void Update () {
		
	}

}
SampleClassB.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public partial class SampleClass : MonoBehaviour {

	public void Func(){
		Debug.Log(this.aaa);
	}
}

実行結果

10

こんな感じで別ソースファイルで定義されているメンバー関数も
partialを使うことで同一クラスのメンバーとして認識されるんですね〜〜

partialなクラスを定義するための条件としては

  • partialというキーワードをつける(当たり前だが)
  • クラス名は統一させる(GameObjectにAddさせるときはソースファイル名も統一させる)

ちなみに同一クラスとして認識されるので、非公開メンバも参照可能っぽい

partialを用いたクラス設計

実際に現場では、partialを用いたクラス設計をした
今回紹介する例としては
partial + Stateパターン

Stateパターンはデザインパターンの1つだが、
partialを利用することで、綺麗に実装することができた!

サンプルソース

Player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public partial class Player : MonoBehaviour {

	public interface IPlayerState {

		/// <summary>
		/// この状態になったときに実行される
		/// </summary>
		void OnEnter();

		/// <summary>
		/// この状態の間、毎フレーム実行される
		/// </summary>
		void OnUpdate();

		/// <summary>
		/// この状態ではなくなるときに実行される
		/// </summary>
		void OnExit();

		/// <summary>
		/// 現在の状態を取得する
		/// </summary>
		/// <returns>現在の状態</returns>
		IPlayerState GetCurrentState();
	}

	private float hp;
	
	private float power;

	private float speed;

	// Use this for initialization
	void Start () {
		//処理
	}
	
	// Update is called once per frame
	void Update () {
		//処理
	}

}
PlayerAttackState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public partial class Player : MonoBehaviour {
	private class AttackState : IPlayerState {

		Player _;
	
		public AttackState(Player player){
			_ = player;
		}
		
		void IPlayerState.OnEnter() {
			//処理
		}
		
		void IPlayerState.OnUpdate() {
			this.Punch(_.power);
		}
		
		void IPlayerState.OnExit() {
			//処理
		}

		IPlayerState IPlayerState.GetCurrentState() {
			return this;
		}
		
		void Punch(float power){
			//処理
		}
	}
}


partialを利用することで、プレイヤーという1つのクラスを複数に分けることができ、
この場合だと、状態別にプレイヤーのクラスを分割することができクラスの整理がしやすい!


また、状態クラスを内部クラス化させているのは、
内部クラスからは外部クラスの非公開メンバーを参照することが可能なため、
状態クラスから、プレイヤーの非公開なステータスにアクセスできたり、、、と割と使い勝手がいい
さらに非公開メンバーなので、それ以外のクラスからは見えないようにカプセル化されていたり
Stateパターンを実装する時には、こんなやり方もありかも?