public, privateとはつまりは公開、非公開のことであります。
今回は"クラス"同士が連携する際のセーフ・ガードの役割を果たす、アクセス修飾子、プロパティについてまとめてみたいと思います。Unity上の使い方についても書いてみました。
プログラム全体を1つの大きな部屋だとすると、クラスは小部屋であり、その小部屋に自由に出入り出来るかどうか?ということです。
これだけだと少し分かりづらいですが、より理解するためにはクラス及び、オブジェクト指向の本質、そしてカプセル化や情報隠蔽を理解する必要があります。
では、なぜこのような修飾子が必要になるのか?について書いていきます。
いくつものクラスが組み合わさって
そもそもオブジェクト指向における"クラス"とは何でしょうか。
設計図のようなもの、と言われますが、要はプログラムを分割するためのものです。
オブジェクト指向の本質とは、いかに仕事、機能ごとに分割して、どのようにそのクラス同士を連携させるか?ということになると思います。
ここでの問題は、まずどう分割するかの作業があり、かつそれを上手くやるには物事の本質を見極める能力が必要であるということ。ここについてはアクセス修飾子とは直接的な関係がないので、後日に"クラス"についてのエントリを書く時に触れたいと思います。そして次に"クラス"によって、全体のコードはバラバラにされているわけなので、実際にはそれらのクラスが連携し合うことで、プログラムが動くということです。
このいかにクラス同士を連携させるか?という問題が、本記事にとって重要な部分です。ここでの注意点は、別に連携させる必要がないなら無理に連携させる必要はないということです。つまり、あるクラスの中だけで処理が完結するのならば、それに越したことはない、ということになります。
クラス間の結合度を低く抑える、という風に言われます。
各クラスが独立していれば最高
さて連携すると言っても、どの程度深く関わるか?という話にもなります。
その関わり方が浅い方がより良い、ということです。
結合度という言葉が出ましたが、これが高くなるとクラス同士が絡み合い、プログラムが複雑になってしまいます。分割して、各部をシンプルにするためのオブジェクト指向なのに、それでは本末転倒です。
ここら辺はクラスをいかに分割するか?というクラス設計も関わります。クラスをどう作るか?という問題が実は一番難しい、というような話もあるようです。
というわけでpublic, privateやプロパティというのは、クラス間の連携具合、アクセスを制御し、安全性を高めるためのものと言うことができます。
身近な例で考える
さて公開、非公開とはどういうことでしょうか?
もし、あなたの家に友達が来たら、玄関を通り家に中に通します。もしも宅急便の配達員が来たら、玄関で荷物を受け取り、家の中には入れさせません。
つまりクラスにおけるprivateとは家の中で、publicとは玄関あるいは外の世界ということになります。(玄関は"外との境界"という解釈です。)
家の中はプライベートな部分なので、誰にでも見せるものではありませんし、親しい人にのみ許されます。要するにウチとソトです。
プログラミングは何らかの事務処理なのですから、今度は市役所に例えてみます。
あなたがそこの市民であるなら、市役所に行って住民票を発行してもらうことが出来ます。しかし、それはあくまで写し(コピー)であり、原本をもらうことは出来ません。
そして、市役所の中には入ることはできませんし、内部に保管されている他人のデータを見ることもできません。あくまで入ることのできるのは窓口だけです。
プログラミングで言えば、
"市民class"のオブジェクトである太郎くんが、"市役所class"の住民票発行メソッドにアクセスして住民票の写しを取得できるが、"市役所class"内にある自分や他人のデータに直接アクセスすることはできないし、ましては変更することは禁じられている、という風に言うことが出来ます。
つまりクラス間のアクセスを制限するために、アクセス修飾子は必要なのです。
privateにすることで外部の人間に触らせないようにする、ということです。
外部の人間が利用したい機能などにはpublicをつけて利用できるようにします。
プロパティは設定画面であり、窓口
プロパティも同様です。
変数をそのままpublicにしてしまうと、グローバル変数になってしまい危険です。つまり、どんなクラスからだろうとアクセスできてしまい、誤代入や干渉が発生してしまう恐れがあります。
クラスの中に収めつつ、外部から安全に変数にアクセスできるようにするにはプロパティが必要になります。市役所の例を先ほど出しましたが、まさに"クラス"における窓口のような存在ともいえる機能です。
変数そのものはprivateにします。しかし、それではクラスの外からアクセスできません。そんなprivate変数へのアクセスの窓口として機能するのが、プロパティです。
つまりプロパティを通してでしか、外部から変数の値を取得したり、変更できないようにすることで"クラス"のカプセル化、情報隠蔽を維持するわけです。またprivate setにすることで、外部からは変更出来ないようにすることもできます。例えば電話番号とかを勝手に変更されたら大変ですもんね。(取得だけ出来る)
private int _money; //クラス内からしかアクセスできない。
public int Money {
get{ return _money;} //外部からのアクセスに対し、代わりに渡す。
private set{ _money = value;}
}
Unity内での実践
ということで、Unityにおいてprivate, publicをどのようにつかっていくかの簡単な実例を出してみたいと思います。
ここでは二つのクラスを考えます。
プレイヤーキャラの操作などを管理する、PlayerControllerと、
ゲーム全体、スコア、残り時間を管理する、GameControllerです。
さて残り時間がゼロになると、ゲームオーバーになるとします。
if( timeLeft <= 0 )
この時、プレイヤーが敵に当たって死ぬ時の演出を時間切れの際に、発動させたいわけです。マリオみたいな感じですね。
Death()メソッドとしますが、これはPlayerController内にあります。
しかし、残り時間の管理はGameControllerがやっているので、時間切れを判断した時点で、GameController側がPlayerControllerにあるDeath()メソッドを呼び出す必要があります。
とにかくまずDeath()メソッドをpublicにしないといけません。
C#だと未指定は全てprivateになります。
public void Death(){ //死亡時の処理 }
それからGameControllerがアクセスするようにする必要があります。
もしstaticを使えば、直接GameController.Death()とかけますし、
あるいは、リファレンスからアクセスすることもできます。
PlayerController playerController;
void Start() {
GameObject player = GameObject.FindWithTag("Player");
if(player != null) playerController = player.GetComponent<PlayerController>();
} //この際、プレイヤーにキャラに"Player"タグを設定しておく
Startメソッドで、シーンがロードされた時にFindWithTagを使って、プレイヤーのゲームオブジェクトを引っ張ってきます。そこからアタッチされているスクリプトに対して、GetComponentを使って、参照を得たPlayerControllerのオブジェクトplayerControllerからアクセスするという方法です。
playerController.Death(); という風に呼び出せるわけです。
このように面倒ではあるんですが、安全性のためには致し方ないというか。
またスコア表示についても考えてみたいと思います。
プレイヤーがコインを取った数を画面上に表示するとします。
コインの当たり判定はPlayer側でやって、カウントもそこでやっています。
画面に表示する処理はGameController側です。
なので、変数はint coinAmountとするとして、GameController側がこれを取得する必要があります。
もしpublic static int coinAmountとすれば、
PlayerController.coinAmountという風に直でアクセスできます。
あるいは、プロパティ自動実装を利用して、
public int CoinAmount{ get; private set;}
という風にして、前述のオブジェクトからアクセスする、という風にも出来ます。
coinText.Text = playerController.CoinAmount ;
という感じですね。
しかし、GameController側にスコアを表示するというメソッドを用意して、Player側がそれを利用する、というやり方も考えられますね。
どちらがいいかをどう判断するかが、オブジェクト指向の難しいところだと思います。
まとめ!
クラス自体がよく分かっていない頃は、アクセス修飾子というものの存在意義がよくわかりませんでした。クラスというものの働きが分からないと、アクセスする、という概念自体がまず理解できないからです。
あとプロパティも意味不明で辛かったですね。要はいろんなソフトにもプロパティがあって、そこでいろいろ設定できる、あのプロパティと基本的に同じなんですけど、なんでわざわざ必要なんだろう、という感じだったので。
でも基本的な考え方については、それなりに分かるようになってきたし、今回このような形で、学んできたことを自分の言葉でまとめられたのでよかったです。
で結論を言えば、やはりどう分けて、どう連携させるか?ということになってくるわけですが、まだまだ難しいですね。鍛錬を続けたいと思います。