4. アクセスレベルを意識する
ネットに転がっているようなサンプルコードでは、フィールドやメソッドのアクセスレベルは大抵の場合publicです。しかし、それは単に説明を簡単にするためであって、実際は何でもかんでもpublicにしてはいけません。
publicということは、そのクラスの外からでもアクセスできてしまうということです。フィールドであれば値を読み書きでき、メソッドであれば呼び出すことができます。それは例えるならドアや窓を全開にした家のようで、「どうぞご自由に侵入してください」と言っているようなものです。
これはなにも悪意ある第三者に攻撃される恐れがあるという話ではなく、それが外で自由に使っても良いものなのか分かりにくくなるからということです。後でそのプログラムを触る人(他のメンバーや未来のあなた)がコードを読んだとき、「このメソッドは外から呼んでいいのか?」「この値は外から自由に書き換えていいのか?」と困ってしまいます。その問題を解決するために無駄な手間がかかるばかりか、うっかり触ってはいけないものに触れてしまってバグを生んでしまうかもしれません。
アクセス修飾子
まず、アクセスレベルは基本的にprivateが前提です。privateはそのクラス内からしかアクセスできない、最も安全なレベルです。そこから、どうしても外に公開しなくてはならないものだけをpublicにします。単にインスペクターに表示されるようにしたいだけなら、publicにするのではなく [SerializeField] 属性をつけましょう。
privateでは子クラスからもアクセスできません。もし子クラスからもアクセスしたい場合はprotectedにします。普段の開発ではこの3つさえ使えれば特に困ることはないでしょう。
// ❌ Bad: 全部publicで丸見え
// 「どうぞご自由に侵入してください」状態
public class Player : MonoBehaviour
{
public int hitPoint;
public int maxHitPoint;
public GameObject bulletPrefab;
public void DecrementHitPoint()
{
hitPoint--;
}
}
// ✅ Good: 必要なものだけ公開する
public class Player : MonoBehaviour
{
[SerializeField] GameObject bulletPrefab; // インスペクターには表示されるがコードからはアクセス不可
int hitPoint; // private (修飾子省略)
int maxHitPoint; // private
public void TakeDamage(int amount) // 外部に公開する必要があるものだけpublic
{
hitPoint -= amount;
}
void Die() // 外から呼ばれたくないのでprivate
{
// ...
}
}プロパティ
外から値を読み取るのは許可したいが、外から書き込みはされたくないという場合があります。これは単にアクセス修飾子だけでは解決できません。そこで、プロパティを使います。
プロパティの前に、getterとsetterについて説明します。あるprivateなフィールドを、privateなまま外から読み書きできるようにしたいとしたら、どうすればいいでしょうか。それは、そのフィールドの値を返すメソッドと、フィールドに値を書き込むpublicなメソッドを作ることです。このメソッドをそれぞれgetter/setterといいます。わざわざフィールドをそのまま公開せずgetterとsetterを使うメリットは、不正な値を弾いたり値を加工したりして安全に読み書きできるようになることです。
// getter/setterを自前で実装する場合
public class Player : MonoBehaviour
{
int hitPoint;
public int GetHitPoint()
{
return hitPoint;
}
public void SetHitPoint(int value)
{
hitPoint = Mathf.Max(0, value); // 不正な値を弾ける
}
}このgetterとsetterを簡単に実装する機能がプロパティです。プロパティを使うと、外部からはあたかもフィールドのようにアクセスすることができます。よく使うのが、getterとsetterに特別な処理を書かない場合に使う自動実装プロパティです。
プロパティの嬉しい点は、getterとsetterのアクセスレベルを個別に設定できることです。setter側をprivateにしてしまえば、読み取りだけを外部に許可することができます。また、そもそもsetterを消すことでクラス内からも変更できないようにもできます。プロパティは単にフィールドをpublicで公開するよりも安全なため、フィールドを公開するときは基本的に必ず使うようにします。
// プロパティを使う場合
public class Player : MonoBehaviour
{
int hitPoint;
public int HitPoint
{
get { return hitPoint; }
private set { hitPoint = Mathf.Max(0, value); } // setterはprivate
}
}// 自動実装プロパティ (特に加工が不要な場合)
public class Player : MonoBehaviour
{
public int HitPoint { get; private set; } // 外から読めるが書き込めない
public int MaxHitPoint { get; } // setterなし: クラス内からも変更不可
}