オブジェクト指向プログラムで
getter/setterメソッドを使わなければならない
10の理由
福盛 秀雄
fukumori at m.ieice.org
JavaやC++などのオブジェクト指向言語でプログラムを書いているときに、単純なメンバ変数を参照したり操作するために
-
anObject.getX() [以後これをgetterメソッドと呼ぶ] とか
-
anotherObject.setY(y) [以後これをsetterメソッドと呼ぶ]
と書くのはなぜだろうと思ったことはないだろうか? int型の変数ひとつを操作するのになぜわざわざメソッドを定義するのだろう?
単純に代入を使えばいいじゃないか?
この文章はそんなあなた(かつての僕も含む)が、getter/setterメソッドを使うべきである理由についてまとめたものである。
ということで早速本論へ。
1. クラス内部のデータ表現を変えた場合でも呼び出し側のコードを変更する必要がない。
詳しい説明はオブジェクト指向の教科書で山のように出てくるはずなので省略。
-
"Effictive C++ Second Edition"[1]の"Item 20: Avoid data members in the
public interface."
-
"C++ Strategies and Tactics"[2]の"2.3 Public Data"(pp.33-36)
-
"Large-Scale C++ Software Design"[3]の"2.2 Member Data Access"(pp.65-69)
などに具体例があるので機会があれば参照してみると良いだろう。
2. クラスを分割することになっても、呼び出し側のコードを修正する必要がない。
1.の派生形と言えなくもないが、上記いずれの文献でも説明されていないパターンのため、別項として取り上げる。
プログラムを作っているうちに最初は小さかったクラスが段々肥大化してゆくことがある。対策としては大きくなったデータ構造を手におえるサイズに分割することだが、getter/setterを使っていれば、これらを分割されたクラスのgetter/setterへの呼び出しに書き直すことで、呼び出し側のコードは変更なしにそのまま使うことができる。データ構造を直接参照しているようなコードでは、分割したデータ構造へのポインタを数珠つなぎに参照するよう、コードを直さなければならない。
3. ポリモルフィズムを使おうとするとgetter/setterメソッドが必要になる。
データメンバを直接操作するコードをいったん書くと、後でポリモルフィズムを使おうとしたときに手も足も出ない。
4. Dynamic Linkingとの合わせ技で、呼び出し側のコードを再コンパイルすることなしに、クラスにメンバ変数を追加したりメンバ変数の構成を後から変更することができる。
互換性を確保したまま、ライブラリをバージョンアップしたい場合にはこれが大事だったりする。さらにポリモルフィズムと組み合わせれば、プラガビリティ(プラグインを導入する感覚で完成後のプログラムに新しいクラスを追加すること)にまで到達できる道が開かれる。
5. メンバ変数のアクセス制御ができるようになる。
getterだけを提供すれば、メンバ変数を読み出し専用にすることができるし、setterだけ提供すれば書き込み専用となる。メンバ変数が読み書き両方を許可している場合でもgetter/setter両方を明示的に書くことで、読み書き両方許可という作者の意図をはっきりとコードに残しておくことができる。
6. データがどこで参照/変更されているかがコード中で見やすい。
getter/setterを使って書かれたコードは単純な代入に比べて、データの参照や操作がどこで行われているかがはっきりとする傾向がある。テキスト検索をするときも有利だしね。
7. メンバ変数の参照や変更を追跡することができる。
getter/setterメソッドにデバッグメッセージを埋め込んだり、デバッガでブレークポイントを設定しておけば、誰がいつデータを参照したり、変更したかを簡単に突き止めることができる。
例えばマルチスレッドで動作するプログラムの不具合を解決するときにはこういった情報がとても大事になる。単純な代入を使っているような場合にはこうは行かないはずだ。
8. 値の正当性チェックを入れることができる。
具体的に言うと、
-
設定される値の正当性をsetterで確認したり
-
クラスが初期化される前にメンバを参照したりなどしていないかをgetterで確認したり
することができる。具体例を示したものとしては、"C++ Strategies and Tactics"[2]の"2.3.1
Representation invariant"(pp.36-37)などがある。
これをさらにすすめると、事前条件(precondition)、事後条件(postcondition)をきちんと定めたプログラミングスタイル、いわゆる「契約によるデザイン(Design
by Contract)」になる。「契約によるデザイン」については"Object-Oriented
Software Construction, Second Edition"[4]の"11 Design by Contract: building
reliable software"(pp.331-410)あたりが詳しい。
9. オブジェクト指向とはそういうものである。
まずは純粋なオブジェクト指向言語の元祖であるSmalltalkに登場ねがおう。
Alan Kayは"The early history of Smalltalk"[5]で以下のように述べている。
「...Smalltalkはコンピュータそのものの概念を再帰的に適用したものである。Smalltalkのオブジェクトは、『コンピュータ』というものを、プログラミング言語の世界で普通に見られるデータ構造や手続きや関数などといったものに分割して、その力を薄めてしまわないよう、コンピュータの持つ全ての能力を再帰的に体現しているものとなっている。」
さらに青木 淳氏の著作"オブジェクト指向システム分析設計入門"[6]からの言葉も引用しておく。
「いち早く皆さんをオブジェクト指向に洗脳するには,皆さんの頭をメスで切り開いて,神経組織網を私と同じにしてしまうことであろう。これは抽象データ型というオブジェクトの考えに違反することと同じである。実際にメスで切り開くことなんかできない。ではどうするかというと,会話や書籍などのコミュニケーション手段を駆使して皆さんの目や耳などに訴えかけて,皆さん独自のそれぞれの考え方(記憶へのアクセス)で,オブジェクト指向の真意を理解(記憶)してもらうことしかできない。これは抽象データ型というオブジェクトの考えに沿うことである。」
以上を読んで分かっただろうか。オブジェクト指向にはデータメンバを外部から直接操作するという考え方がない(あるいは排除されている)ということだ。C++やJavaなどでメンバ変数の直接操作を許すのはCからの移行性に配慮した結果に過ぎないと言い切ってしまっても良いだろう。
10.
...ということで、10番目は皆さんへの宿題としておこう :)
参考文献
[1] Scott Meyers, "Effective C++, Second Edition", Addison-Wesley, 1997
[2] Robert B. Murray, "C++ Strategies and Tactics", Addison-Wesley,
1993
[3] John Lakos, "Large-Scale C++ Software Design", Addison-Wesley,
1996
[4] Bertrand Meyer, "Object-Oriented Software Construction", Prentice
Hall, 1997
[5] Alan C. Kay, "The
Early History of Smalltalk", CM SIGPLAN Notices, Volume 28, No.3
March 1993
[6] 青木 淳, "オブジェクト指向システム分析設計入門", http://www.sra.co.jp/people/aoki/IntroductionToOOAOOD/index.htm,
1993
Hideo Fukumori (fukumori at m.ieice.org)
ホームページへ戻る