データ抽象化とカプセル化

この節の要点

  • 内部のデータ構造を意識せずに処理を利用できるようにすることをデータ抽象化と呼ぶ。
  • アクセス修飾子を用いて、他のクラスから参照してほしくない箇所はprivateを設定する、メソッドを駆使してデータを受け取るなどして、内部処理やデータを意識しない状態にする(誤解を恐れずに言うのであれば、外部から参照できる箇所を適切に設定する)ことをカプセル化と呼ぶ。

図1のUserクラスを使う場合、図4の6行目、7行目のように作成したオブジェクトのフィールドを直接参照して値を代入していました。この場合、別モジュール(クラス)の内部データを直接編集していることになり、利用するクラス間でのモジュール結合度が高まります。

実際のところ、フィールドとメソッドの抽象度を比較すれば、明らかにフィールドの抽象度の方が低く具体的であり、メソッドの抽象度の方が高いわけです。例えば、図3のUserクラスでは、名前を姓familyNameフィールドと名firstNameフィールドに分割してより詳細な情報として保持しており、それらのなんらかの具体的な加工方法を考えなければなりません。

ですが、getUserNameメソッドが提供する機能はユーザ名を返却することであり、メソッドを呼び出す側からすればユーザ名が返却されることだけを考えるだけですみます。すなわちクラスを使う側からすれば内部データを気にする必要がなくなるわけです。従って、仮にfamilyNameフィールドの名称を変更したい、もしくはgetUserNameメソッドの内部処理を変更したいという要望が生まれたとしても、メソッドを介したやり取りを心がけていた場合、変更箇所が少なく済むことになるわけです。

フィールドを介するよりもメソッドを介することで構造化プログラミングにおいても要点であった抽象化が図られるわけです。

図3のUserクラスについて、フィールドを使ったデータ初期化を、setUserNameメソッドを使ったデータ初期化に変更したものを図12に示します。

public class User {

	private String familyName;
	private String firstName;

	public void setUserName(String name) {

		String[] nameParts = name.split(" ");
		
		familyName = nameParts[0];
		firstName = nameParts[1];
	}

	public String getUserName() {

		return familyName + " " + firstName;
	}
}
図12:図3のプログラムにフィールドを使ったデータ変更を制限してsetUserNameメソッドを導入

このように、内部構造を意識することなくデータ(オブジェクト)を扱うことができるようにクラスを設計することをデータ抽象化(Data Abstraction)と呼びます[13]。

このデータ抽象化の中でも特に、クラス内のデータ構造を直接利用できないようにアクセス制限し、その代わりにメソッドを介してやり取りすることで具体的な構造を包み隠すことをカプセル化(Encapsulation)と呼びます[14]。

[13]: L. Barbara, ``Keynote address – data abstraction and hierarchy'', ACM SIGPLAN Notices. ACM, 23 (1988)

[14]: A. Shalloway, and J.R. Trott, ``- デザインパターンとともに学ぶ - オブジェクト指向のこころ'', 丸善出版 (2014)