検索

キーワード


【Java】オブジェクト指向の基礎概念「カプセル化」と「隠蔽」の考え方

  • 公開日:2020-10-15 14:53:41
  • 最終更新日:2021-01-26 09:30:41
【Java】オブジェクト指向の基礎概念「カプセル化」と「隠蔽」の考え方

こんにちは、駆け出しプログラマーの若江です!

ここでは初学者として学習を終えた私からアウトプットの意味も込めて、

カプセル化と隠蔽について紹介させていただきます。

できる限り初学者が理解しやすい内容として紹介させていただくので、参考となれば幸いです!


関連記事リンク:オブジェクト指向の紹介 / クラスの基本を紹介 / コンストラクターの紹介 / メソッドの紹介 / アクセス修飾子の紹介 / フィールドの紹介

カプセル化と隠蔽の目的と実現方法

カプセル化はオブジェクト指向の主軸となる概念の1つです。

そのためオブジェクト指向を理解する上で、カプセル化の理解は必須と言えます。

ここではオブジェクト指向を構成する主要素の1つであるカプセル化について紹介します。


カプセル化の目的

カプセル化とは、独立性が高くて使い方がわかりやすいブジェクトを作成することです。

オブジェクト指向の主要な概念に含まれるカプセル化は、

プロジェクト全体をシンプルかつ整頓された状態に保ち、

プログラムの変更や追加によって関連オブジェクトが受ける影響を軽減することができます。


より具体的にはオブジェクトのカプセル化を行うことによって以下のメリットを得ることを目的としています。


カプセル化の目的

・各オブジェクトの役割を実行するために必要となるフィールドやメソッドを同じクラスにまとめることで、

 個々のオブジェクトの使い方がわかりやすく明確になる。

 また、エラーが発生した際にどこでエラーが起こっているかわかりやすくなる。


・オブジェクトの利用者は、オブジェクトがどのように処理を行って動作しているのか?ということを

 理解しなくてもオブジェクトを使うことができる。


・オブジェクトの独立性が高くなるとプログラムの一部に変更が加えられても、

 その変更による影響の範囲を同オブジェクト内で留めることができる。



上記メリットは人の体を例にするとわかりやすいかもしれません。

例えば、「肺」と言えばおそらく誰もが呼吸という役割を想像できると思います。(役割が明確)

しかしどのように作用しあって肺が体へ酸素を取り入れているか?は知らなくても呼吸ができます。(利用のし易さ)

また少し呼吸がし辛いなと思えば肺の不調を疑えるはずです。(エラー個所の特定)

肺は肺として独立した機能を持っているため、もし肺を手術することになったとしても

他の器官の手術も一緒にしなくてはならないといったことは起こりにくいです。(変更による影響)


オブジェクトをカプセル化することは、上記のようにオブジェクトの利用者へ恩恵を与えます。

更には開発の負担軽減や、開発コストの削減にもつながります。

もしカプセル化が甘ければ、

役割が曖昧でいざ使う場面になってもどのように使えるのか?を読み解いてからでないと使えず、

曖昧さが故にエラーが起こった場所の特定が難しい!といったオブジェクトなります。

更に機能を変更しようと思っても、その変更が影響を与える範囲の広さがわからないといったことにもなります。

これではプログラマーの負担が大きく、大規模なチーム開発になるほど混乱が起こり収拾がつかなくなるため、

オブジェクト指向にとってカプセル化は重要な要素と言えます。


カプセル化(関連要素をまとめる)

先述したカプセル化を実現するための要素の1つで、

オブジェクトが持つ役割を実行するために必要なフィールドやメソッドを同じクラスへまとめる例を

「動物園」クラスと「動物」クラスのサンプルコードで見てみましょう。


◆1つのクラスへメソッドやフィールドをまとめる例

【動物園クラス】 動物園名と動物園の広さを参照できるクラス

public class Zoo {
	private String name = "XXX動物園";
	private int parkSize = 1000;

	public String getName() {
		return name;
	}
	public int getSize() {
		return parkSize;
	}
}

【動物クラス】 動物が、食べる動作と眠る時間を保持するクラス

public class Animal {
	private int satisfaction;
	private int sleeptime;
	
	public int eat() {
		return this.satisfaction += 1;
	}

	public int sleep(int sleeptime) {
		return this.sleeptime = sleeptime;
	}
}


上記のように動物園クラスと動物クラスで役割を分けられますが、

カプセル化を考慮しない場合は、動物クラスから動物園の名前へアクセスするなどとなってしまうことも考えられます。


◆カプセル化を意識していないクラスの例❌

public class Animal {
	private int satisfaction;
	private int sleeptime;
	private String parkName = "XXX動物園";

	public int eat() {
		return this.satisfaction += 1;
	}

	public int sleep(int sleeptime) {
		return this.sleeptime = sleeptime;
	}

	public String getParkname() {
		return parkName;
	}
}


小規模の開発であれば影響はまだ少ないかもしれません。

しかし大規模プロジェクトになるほどクラスの数が膨大なプロジェクトとなり、

クラスAメソッドがクラスBフィールドを使うなどといったことが頻繁に行われていると、

後日一部の変更があった場合でも影響を与えうる範囲が広まり、

エラーが発生した場合に該当個所の発見に手間取るなどのリスクが増えます。

開発効率を高められることがオブジェクト指向のメリットなので、

1つのクラス内で処理が完結するように「独立性」を高めなくてはなりません。


独立性

独立性とは先述のように関連性のあるメソッドやフィールドを1つのクラスにグループ分けした上で、

更に外部からのアクセスを制限(隠蔽)することで、

そのクラスの役割を明確にしながら外部からの意図しないアクセスを制御することを指します。

オブジェクト指向に則して、限りなく独立した1つのオブジェクト(モノ)を作る考え方です。


独立したクラスを作ることで、クラスごとの役割が明確になり管理が容易になります。

またプログラムの変更があった際も繋がりのある他クラスに対する影響は少なくて済みます。



隠蔽とは?

隠蔽とは外部のクラスから自クラスのフィールドへ直接アクセスできないようにすることです。

外部からのアクセスを制御することで、意図しないインスタンス変数の更新などを防ぐことができます。

外部クラスからのアクセスを制御するためにはアクセス修飾子の private を使います。


◆ 外部からアクセスできないインスタンス変数を持つクラス例

public class ObjectOrientedClass {
	
	private int num;
}


例の変数 num は外部からアクセスできませんが、全くアクセスがない場合は存在している意味もありません。

インスタンス変数を隠蔽した場合は、自クラスのメソッドからインスタンス変数へアクセスします。

隠蔽したインスタンス変数へアクセスするメソッドのことを「アクセサメソッド」と呼び、

外部クラスからのアクセスがあることを想定しているケースが多いため、

最もよく利用されるアクセス修飾子は public です。


代表的なアクセサメソッドはへインスタンス変数へ値を渡すことができる「setter」と、

インスタンスメソッドの値を返すことができる「getter」です。

ちなみに getter / setter のメソッド名はインスタンス変数名の頭を大文字とした

「getインスタンス変数名」「setインスタンス変数名」と命名するのが一般的です。

 例)「int num」に対するメソッド名 ▶ 「getNum()」「setNum()」


◆アクセサメソッドの作成例

public class ObjectOrientedClass {	
	private int num;

	// アクセサメソッド getter
	public int getNum() {
		return num;
	}

	// アクセサメソッド setter
	public void setNum(int num) {
		this.num = num;
	}
}


※開発環境で Eclipse を利用している場合は getter と setter を自動生成することができます。

 対象クラス内で右クリック ▶ ソースを選択 ▶「getter および setter の生成」をクリック ▶

 getter と setter がアクセスするインスタンス変数を選択 ▶ 付与するアクセス修飾子を選択 ▶ 「生成」をクリック



カプセル化したクラスの使い方

先述紹介した setter と getter ではインスタンス変数へ直接アクセスして

値を参照したり変更しているのと変わらないためカプセル化とは言えません。

特に setter に関しては今の状態では独立性を保つ観点ではザルのような状態となっています。


一般的に setter では、インスタンス変数へ渡す値を管理させるようにプログラムします。

例えば受け取る値の範囲を制限するなどです。

◆ setter の記述例

【カプセル化クラス】 setter でインスタンス変数へ渡せる値を 1〜9 に制限

public class ObjectOrientedClass {
	private int num;

	public int getNum() {
		return num;
	}

	// アクセサメソッド setter:1〜9 はその数字を渡し、それ以外は0 を渡す
	public void setNum(int num) {
		if(0 < num && num < 10 ) {
			this.num = num;
		}
		else {
			this.num = 0;
		}
	}
}

【実行クラス】

public class Test {
	public static void main(String[] args) {

		ObjectOrientedClass test = new ObjectOrientedClass();
		test.setNum(9); // 1〜9 が受け取り範囲のため 9 を受け取る
		System.out.println(test.getNum()); // 結果:9 を表示
	}
}


このように setter の処理を記述することで値の変更可能範囲を制限することができます。

また、setter は持たせずにコンストラクターで設定した初期値を参照するのみに制限することも可能です。

◆ setter を書かないカプセル化クラスの例

public class ObjectOrientedClass {
	private int num;

	// コンストラクターで初期値の設定
	public ObjectOrientedClass(int num) {
		this.num = num;
	}

	// アクセサメソッド getter
	public int getNum() {
		return num;
	}
}


クラスの用途に応じて適切なカプセル化をすることが可能です。

上記で基本的なカプセル化ができるようになりましたので、実際に利用する例を確認しましょう。

冒頭でも紹介した通り、カプセル化をしたオブジェクトを利用する人は、

オブジェクトの具体的な処理内容を把握することなく利用することができます。


◆カプセル化クラスの利用例

【動物園クラス】

public class Zoo {
	private String name = "XXX動物園";
	private int proceeds;
	private int extra;

	public String getName() {
		return name;
	}

	// 餌やり料
	public int feed() {
		return extra += 50;
	}

	// 動物園の売り上げ計算
	public int proceedsMethod(int entranceFee, int person) {
		return proceeds = entranceFee * person + extra;
	}

	// 売上額のデータ取得
	public int getProceeds() {
		return proceeds;
	}
}

【実行クラス】 動物園クラスの売り上げを呼び出し

public class Test {
	public static void main(String[] args) {

		Zoo test = new Zoo();
		test.feed();
		test.proceedsMethod(1000, 10000);
		System.out.println(test.getProceeds());
	}
}


Zoo クラスで売り上げ算出方法は意識せずに動物園の売り上げを出力する例です。

feed メソッドを呼び出すと売り上げに 50 を追加します。

Zoo クラスのメソッドを利用する際にアクセス修飾子に注意は必要ですが、

基本的に public なメソッドの使用方法を知れば良いため、利用者は Zoo クラスの使い方が理解しやすくなります。


このように公開範囲を定めることで、

第三者がオブジェクトを利用するにあたり「知る必要のある部分」と「知らなくて良い部分」などを分けて、

クラスの使い方をシンプルにすることができます。

また公開しない範囲は不用意なデータの書き換えが起こらないため、

オブジェクトの情報を守ることもできます。



まとめ

カプセル化は、作用しあうメンバー(メソッド・フィールド)を1つのクラスにまとめて、

インスタンス変数を適切に隠蔽することで成り立ちます。

ただし手当たり次第に隠蔽をすれば良い訳ではなく、

オブジェクト指向の考え方に則した形で適切にまとめて、隠蔽することを本来のカプセル化です。

オブジェクト指向の概念を理解した上でカプセル化ができるようになりましょう。


関連記事リンク

オブジェクト指向の紹介 / クラスの基本を紹介 / コンストラクターの紹介 / メソッドの紹介 / アクセス修飾子の紹介 / フィールドの紹介



【著者】

若江

30代で異業種となるIT業界へ転職した駆け出しのプログラマです。これまで主に Java や Ruby、HTML/CSS を使って学習を目的としたショップサイトや掲示板サイトの作成を行いました。プログラマとしての経験が浅いからこそ、未経験者の目線に近い形で基礎の紹介をしていきたいと思います。

よく読まれている記事
【Java】JSPでタグライブラリを使う(JSTL)

【Java】JSPでタグライブラリを使う(JSTL)

こんにちは。エンジニアの新田です!ここでは、システムエンジニアとして働いている私が、システム開発手法や開発言語について紹介していこうと思います。今回は、JSPの標準タグライブラリ「JSTL」について紹介します。Javaについて勉強している方、Webアプリケーションを構築したいと思っている方の参考になれば幸いです!関連記事リンク: 【Java】JSPの基本的な構文/【Java】JSPのアクションタグ

【Java】Stringクラス文字列を操作するメソッドの使い方まとめ!実例も紹介!

【Java】Stringクラス文字列を操作するメソッドの使い方まとめ!実例も紹介!

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者✅にも、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!そもそもStringとは何?『 String 』... Java言語において文字列のデータ型を指します。基本デ

【Java】文字列の置き換え(String#format)!エスケープシーケンスのまとめも!!

【Java】文字列の置き換え(String#format)!エスケープシーケンスのまとめも!!

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者✅にも、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!Stringクラスformatメソッドの文字列整形【java.utilパッケージ】Formatterクラスfo

【Java】文字列格納後に変更可能!?StringBufferクラスとStringBuilderクラス!

【Java】文字列格納後に変更可能!?StringBufferクラスとStringBuilderクラス!

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者にも✅、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!文字列を扱う3つのクラス【java.langパッケージ】java.langパッケージの文字列を扱うクラスにはS

【Java】値?変数?型??しっかり解説!『データ型(プリミティブ型と参照型)』

【Java】値?変数?型??しっかり解説!『データ型(プリミティブ型と参照型)』

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者✅にも、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!プリミティブ型と参照型プログラム開発では型を持った変数を使ってデータのやり取りをしますが、データ型によって仕様