はじめに
前回までにクラスやメソッド、プロパティなどについて扱いましたが、これらのデータは自由に上書きしたり参照することができました。しかし、中にはいじってほしくないデータがあると思います。今回はアクセスレベルを変更する修飾子について扱います。加えてカプセル化についても扱います。
お品書き
今回扱う内容です。
1. アクセス修飾子
クラスを作成するのはいいのですが、中にはいじらないでほしいデータがあるかもしれません。決められた定数を使いたい場合、外部からその値を変更されるのは嫌だと思います。そんな時に使うのがアクセス修飾子(アクセス可能度合い) です。アクセス修飾子にはこのような種類があります。
アクセス修飾子 | 意味 |
---|---|
public | どのクラス・プロジェクトからでもアクセス可能 |
protected | そのクラス内と派生クラス、同じプロジェクトからアクセス可能 |
private | そのクラス内からのみアクセス可能 |
他にも friend や dim などがありますが、よく使われるのはこれです。クラスやそのメンバ変数はデフォルトでは private になっています。なので、private にしないとインスタンス化しても使用することができません。前回、public を付けたのはその理由です。
2. カプセル化
苦い薬をカプセルに包み込むように、大事なデータもカプセルに包み込むことができます。カプセルに包まれた中身は、外と接触することはありません。このようにしておくことで、外から直接データをいじられることがありません。
外からいじる人はだれかが問題です。この場合、外からいじるのはユーザです。ユーザがデータを入力すると、そのデータは変数という形で内部に保持されます。保持されたデータはクラスやメソッドを通り、処理されて再びユーザに見える形で出力されます(バックグラウンドの場合は見えません)。
例えば整数型のデータを扱いたいのに文字列型を代入すると、例外が発生してしまいます。そうならないように、正しい形式かを判定できるようなメソッドを経由させて、再度変数にデータを渡せば解決です。つまり、身代わり用の変数を public にして、システム本体で使う変数を private にすれば、ユーザから内部の変数にアクセスされることを防ぐことができます。
ユーザからは内部が見えないようにして、システムだけでアクセスできるような情報の集まりを作ることがカプセル化です。もちろん、ユーザからだけでなく、ほかのプログラムからの干渉も防ぐことができます。しかも、カプセル化されていればシステム的に(内部的に)複雑な処理であっても、ユーザから見れば簡単に見えるようになるのも特徴です。
3. カプセル化を使った実装
前回作った人間クラスを書きかえてみます。UIデザインはそのままで、ロジックだけ編集します。MainWindow.xaml.cs はこんな感じです。
public partial class MainWindow : Window { //クラスをインスタンス化(実体化) Human human = new Human(); public MainWindow() { InitializeComponent(); } private void 登録(object sender, RoutedEventArgs e) { // Human クラスの public メソッドへアクセス human.身長入力(height.Text); human.体重入力(weight.Text); human.年齢入力(age.Text); human.名前入力(name.Text); MessageBox.Show("人間データを登録しました"); } private void 年をとる(object sender, RoutedEventArgs e) { // Human クラスの年を取るメソッドを実行 human.年を取る(); MessageBox.Show("あれから1年が経った..."); } private void 情報開示(object sender, RoutedEventArgs e) { MessageBox.Show(human.表示()); } }
人間クラスはこんな感じです。
class Human { // 直接ユーザからいじられないように保護する private int 身長; private int 体重; private int 年齢; private string 名前; public int isInt = 0; //int型かどうかの判定後に代入 // ここにユーザからのデータを入力してもらう // 特に返り値はないので void public void 身長入力(string value) { bool result = int.TryParse(value, out isInt); if (result == true) { this.身長 = isInt; } else { MessageBox.Show("整数を入力してください"); } } public void 体重入力(string value) { bool result = int.TryParse(value, out isInt); if (result == true) { this.体重 = isInt; } else { MessageBox.Show("整数を入力してください"); } } public void 年齢入力(string value) { bool result = int.TryParse(value, out isInt); if (result == true) { this.年齢 = isInt; } else { MessageBox.Show("整数を入力してください"); } } public void 名前入力(string name) { this.名前 = name; } public void 年を取る() { this.年齢 = this.年齢 + 1; } public string 表示() { return $"身長:{this.身長} \n体重:{this.体重} \n名前:{this.名前} \n年齢:{this.年齢}"; } }
これで実行するとこのようになります。
数値が入力されていないとメッセージが表示されますが、登録自体は行われます。ただし、登録できなかった部分は 0 になります。
本来はここの処理も考えないといけないのですが、アクセシビリティが動いているかどうかだけチェックしたかったので、手抜きです(笑)。
直接触らせたくない部分を private にしておくことで、その変数に値を保持したい場合は1度インスタンス先のクラス内を経由させる必要があります。そのクラス内でエラー処理などを行うことで、意図しない動きを事前に阻止できます。今回の場合は入力された値が数値かどうかを判定するメソッドを用いています。
bool 変数 = int.TryParse(入力文字列, out 文字から整数に変換);
これは文字列を入力し、数値として扱える場合はその変換した値を out で変数に出力できます。さらに関数自体の返り値は bool で True , False です。変換できない場合は False になるのでその時の処理は「整数でない」ことを意味しています。また、そのまま int型変数にアクセスするとエラーになるので、変数をいじることはしていません。
おわりに
カプセル化でユーザの操作できる部分をなるべくシステム自体から離すことによって、システムを保護できます。加えてユーザは中身を知る必要がなく、簡単な操作性を実現できます。アクセシビリティを活用することは結構大事だと思います。