オブジェクト指向超入門〜第4回〜

オブジェクト指向では構造体の問題点を解決するための仕組みが提供されています。前回の記事で構造体での問題点として挙げた事は全て、構造体のメンバを直接いじれるのが原因で起こります。どういう事でしょう。

問題点1では構造体を使っている箇所で mm に 13 という値を直接代入しています。問題点2では mm や dd に値を代入したり、あるいはその値を参照している全ての箇所に変更の影響が及びます。だったら mm や dd などの構造体のメンバを使えないようにしてやれば良いのです。その為にアクセス制御という考え方が導入されました。
変数宣言の前に private と付けるとメンバを直接参照したり値を代入する事は出来なくなります。そのかわりにメンバを使うための関数を提供してあげます。

class MyDate {
  // この変数を直接使うのは許さん
  private int mm;

  // 変数を使いたかったら関数を呼びなさい
  public int getMm() {
    return mm;
  }
  public void setMm(int mm) {
    this.mm = mm;
  }
}

これまで変数の集合でしかなかった構造体に新たに関数がメンバとして加わりました。today.mm = 12; とやっていた箇所は、オブジェクト指向では today.setMm(12); と書くようになります。
問題点1のように today.setMm(13); とやられてしまった場合にどうするかというと、setMm という関数の中でエラーを返すなり、翌年の1月になるようにyyとmmの値をうまいこと調整するなりという処理をやってあげればいい訳です。とにかく13月という無効な日付を保持する状態になってしまう可能性を排除してやります。

これまで、月に13を代入するようなバカな事をするのは、そんな事をする方が悪いに決まってるという考え方でした。オブジェクト指向では、クラスを使う側はどんな事をやらかすかわかったもんじゃない、と考えます。日付を保持するというのがクラスの役目であるなら、変な日付が格納された状態になってしまう可能性を排除し、どこぞのバカがトンデモない使い方をしても変な日付が格納される事がないようにしておきます。それは使う側ではなくクラス自身の責任だと考えるのです。

問題点2では日付が年月日の3つの値を持つ事を前提に today.setMm(12); のようなコードを書いていたのに、クラスのメンバに mm が無くなってしまいました。この場合 setMm を呼んでいる全ての箇所を変更して回る必要はなく、setMm という関数自体はそのまま残して、その中身を(ddd という変数に適切な値を格納するように)書き変えてあげるだけで済みます。
こうなるともはや、日付を表現するために年月日の3つの値を持つという事実は、クラスを使う側にはどうでもよくなります。実際この例では、3つの値を持つ前提でクラスのメンバ関数を使っていましたが、実は変数は2つしかありませんでした、というオチですよね。
あるデータを表現する為にどんな変数をいくつ使って実現するかはクラス内部の都合であって、使用者はそんな事は知ったこっちゃないし知る必要もありません。使用者にとって大事なのは setMm という関数の呼び方だけです。
こういうのを実装の隠蔽とかクラスのカプセル化といって、オブジェクト指向のチョ〜重要な考え方です。

実装詳細を隠蔽すれば問題点3も解決します。提供されている関数を使わずに自前で処理を書く手段が無くなってしまう訳ですから、関数が使われている事は完全に保証されます。