ITエンジニアのプレイングマネージャー化応援サイト

11.カプセル化

この記事では、弊社の新人エンジニア研修の参考にJava8を解説します。

前回は継承(拡張)について解説しました。

今回はカプセル化について解説します。

カプセル化はデータ隠蔽とセットなのでデータ隠蔽についてもお話しします。

これらもオブジェクト指向特有の概念です。

 

カプセル化とは、関連する情報と処理を1つにまとめることです。

オブジェクト指向はプログラムを部品と考えて、その部品の組み合わせで大きなプログラムを作るのでした。

例えば、車でもパソコンでも関連する部品は近くに配置されていますね。

ですのでエンジン廻りだけを取り替える、デスクトップパソコンの電源ボックスだけを取り替えるということが可能です。

物理的なモノの場合は、距離の問題がありますから、関連の近いものを、近いところに並べるというのは自然です。

しかし、情報の世界はそうではありませんから、気をつけないとグチャグチャになりがちなのです。

 

データ隠蔽とは、クラスのメンバ(フィールドやメソッド)の公開範囲をできるだけ限定することです

私たちは個々の部品、車やパソコンの中身(やそのデータ)に直接触れることができるでしょうか?

もちろん、強引にやれば可能ですが(良い子の皆さんはぜひ試してみてください

保証適用外になりますね。

逆に言えば、中身に直接触れさせないことによって部品と更には皆さんを守っていると言うこともできます。

このイメージがデータ隠蔽です。

この記事では、カプセル化にデータ隠蔽の意味も含めて解説します。

 

1.カプセル化の意義

カプセル化とは「関連する」情報と処理を一つのクラスにまとめることでした。

「今まで作ったクラスはどれもメンバとしてフィールドとメソッドを持っているのでカプセル化ができている」そうあなたは思ったかもしれません。

ポイントは“関連する”というところです。

例えば次のような一台のノートパソコンを表すクラスはどうでしょうか?

class LaptopPC{
 int id;
 String name;
 String companyName;
 String companyAdress;
 String companyTelephoneNumber;
 ・・・
}

IDと名前までは良いとしても会社名や会社住所、会社電話番号などはLaptopPCクラスが持つべきデータではないですね。

Companyクラスを別途作るべきでしょう。

クラスの設計の考え方には単一責任の原則というものがあります。

1つのクラスの仕事は1つにせよということです。

上記の例では会社の住所や電話番号を取得するにもLaptopPCクラスに問い合わせるということになってしまいます。

また、社名が変更になった場合や会社が引っ越した場合にはLaptopPCクラスを修正することになってしまいますね。

 

上記の例とは逆に、クラスに一切フィールドを持たせずに、メソッドだけでクラスを作成することもカプセル化の考え方に反しています。

手続き思考の関数やサブルーティンになってしまいます。

 

2.データ隠蔽の意義

データ隠蔽の意義は、不用意にデータに触らせないことにありました。

例によって、Javaの標準APIにその範を求めましょう。

前回、StringクラスのhashCodeメソッドまで遡りましたね。

StringクラスのhashCodeメソッドを使ってこんなことができます。

<結果>

65
2080

コンソールの1行目に表示されたのは文字Aの文字コードを10進表記したものです。

しかし、2行目はそんなに単純ではありませんね。

では、IDEを使ってこのStringクラスのhashCodeメソッドの定義をさかのぼりましょう。

以下のコードにあるhashは

とありますので、デフォルトは0です。

また、valueは以下の通りStringのインスタンスを格納するchar型の配列です。

さらにこれらのフィールドは”private”で宣言されていることも注意して下さい。

その上で以下のhashCodeメソッドを読んでみて下さい。

このソースコードから分かるのは、先の65という数値は

31 *  0 + 65

の結果であり、

2080という数値は

31 *  65 + 65

の結果であるということです。(なぜ、31を掛けるのか、興味のある方はググってみてください)

 

ここで本題のカプセル化に戻ります。

hashは以下のように”private”で宣言されていました。

つまり、非公開ということで、「このクラスからのみアクセスできるフィールドである」という宣言です。

他クラスからのアクセスを拒否するという意味です。

ですから、以下のようなことはできません。

エラーが出ているはずです。

強引に実行すれば、

Uncompilable source code – hashはjava.lang.Stringでprivateアクセスされます

と出力されます。

ハッシュ値の性質からそれを強引に書き換えられたとしたらセキュリティ問題や整合性の問題が発生することは容易に想像できるのではないでしょうか?

つまり、このようにフィールドの値を更新するときには必ずメソッドを使って更新し、フィールドには直接アクセスできなくすること」もデータ隠蔽です

公開すべきものと非公開にすべきものを明確に分け、公開したものだけを使ってプログラミングをするようにプログラマに促しているのです。

 

3.アクセス修飾子

private 修飾詞は、他のクラスからアクセスできない(不可視)という意味です。

このように、他のクラスからインスタンス変数を隠すことを「データ隠蔽」といいました。

また、修飾子とは、クラス、フィールド、メソッドの性質を指定するものをいうのでした。

その中でも特に、アクセスを制御するためのものをアクセス修飾子と呼びます。

フィールドとメソッドのアクセス修飾子を公開範囲の広いものから狭いものに並べて一覧にすると以下のようになります。

アクセス修飾子 公開範囲 クラス図での表記
public どのクラスからもアクセスできる +
protected 現在のクラスとサブクラスからアクセスできる #
なし 現在のクラスと同じパッケージのクラスからアクセスできる ~
private 現在のクラスからだけアクセスできる

以下の図は、クラスAの3つのフィールドに対して、どのクラスからアクセス可能かを矢印(➞)を使って示しています。

同じクラスのメソッドからは全てのフィールドにアクセス可能です。

同じパッケージにあるクラスBからはアクセス修飾子が“なし”のフィールドにアクセス可能です。

クラスCはクラスAのサブクラスなので、アクセス修飾子が“protected”のフィールドにアクセス可能です。

この図にはありませんが、“public”のフィールドには全てのクラスからアクセスが可能です。

アクセス修飾子

図解:public以外のアクセス修飾子

 

公開範囲外からのアクセスはコンパイルエラーとなります。

 

ここでは、オリジナルなクラスにデータ隠蔽を施してみます。

例えば、カジノゲームを作成するとします。

プレイヤー(Player)は最初にチップの残高(balance)を1,000持ってゲームスタートです。

プレイヤーはこの残高からチップを引き出すことができますが、残高がマイナスになることはできません。

それを表現したのが以下のサンプルプログラムです。

<結果>

現在の残高です:800
残高不足です

このようにカプセル化を使ってフィールドの値を守ることができるのです。

他のプログラマ(含む将来のあなた自身)に設計者が意図しない操作をさせない、それがカプセル化の効果です。

 

また、フィールドにアクセスするためのメソッドをアクセサメソッド(accessor method)といいます。

Javaの基本的なクラス設計は、フィールドはprivate、メソッドはアクセサメソッドとして、publicにするというものです。

 

アクセサメソッドを使ったサンプルプログラムとして、足し算電卓のクラスを作成してみます。

あくまで説明のためのサンプルですのでまどろこしい処理ですがご容赦ください。

※メソッドgetNum1,2は使っていません。

<結果>

136

アクセサメソッドもIDEで簡単に挿入できますので試してみてください。

このアクセサメソッドについては、後でJSP/サーブレットを学ぶ際に大活躍しますので、お楽しみに。

※ここでは、メソッドは無条件にpublicにすべきとしているように感じられるかもしれません。しかし、本当は違います。メソッドをprivateにした方が良い場合というもの存在します。例えば、同じクラスからしか使わないメソッドはprivateにします。メソッドをpublicにするデメリットは、そのメソッドを使う他クラスがあった場合にメソッドのインプット・アウトプットを変更しづらくなってしまうということが挙げられます(依存性の問題)。余計な公開はしないというのがデータ隠蔽の考え方です。

 

4.クラスとインターフェースのアクセス修飾子について

クラスやインターフェース(未出)もアクセス修飾子を使ってパッケージ外からのアクセスを制御することができます。

公開するか公開しないかの2択ですので分かりやすいですね。

アクセス修飾子 意味
public 全てのパッケージからアクセス可能
なし 同じパッケージからのみアクセス可能

ここでの注意点としては、クラスのアクセス修飾子はフィールドとメソッドのアクセス修飾子より優先されるということです。

つまり、例えばフィールドやメソッドをpublic宣言してもクラスがpublic宣言されていないと、そのフィールドやメソッドは他のパッケージからはアクセスできません。

そもそも、クラスにアクセスできないわけですから当然と言えば当然ですが。

また、public修飾子をつけられるクラスは1つのjavaファイルの中に1つだけです。

さらに、public修飾子をつけたクラス名とファイル名は一致する必要があります。

これまでのサンプルプログラムもそうなっていたことに気づかれた方はいますか?

 

5.final修飾子とは

※この話題は研修時間の都合によりスキップすることがあります。

では、次にカプセル化とは話がずれるのですが、final修飾子についてお話ししたいと思います。

これまでもちょくちょく出てきたとは思うのですが、ここでまとめです。

finalは「最後の」という意味ですが、まさにそんな感じです。

これまでの学習では、

クラスMathのPI(標準API)がありました。

public static final double PI

初期化したら変更できない定数になるのでしたね。

また、このページの上で文字列を格納するvalueというchar配列がfinalです。

文字列はイミュータブルで変更できないというのはこのように宣言されているからです。

 

また、クラスにfinalをつけるとサブクラスを作れなくなります。

finalなクラスの代表例として、String(標準API)が挙げられます。

上記のリンクから標準APIに移動してみてください。(あるいはIDEでソースコードを見てください)

public final class String

となっていますね。

つまり、Stringを拡張してオリジナルなサブクラスは作れないのです。

誰もが勝手に自分仕様の文字列クラスを作成すると混乱するからでしょうね。

それから、Mathもfinalクラスです。

 

さらに、メソッドにfinalをつけるとサブクラスでオーバーライドできないメソッドになります。

クラスは継承させたいが、メソッドはオーバーライドさせたくないケースということになります。

具体的にはオーバーライドされたらまずいメソッドにfinal宣言をするケースはあり得ると思います。

しかし、新入社員のうちは特に気にしなくてよいでしょう。

 

これをまとめると以下のようになります。

<finalキーワードの意味>

finalクラス 継承できない
final変数(フィールド、ローカル問わず) 定数(再代入できない)
finalメソッド オーバーライドできない

できない、できない、できない、とまさにfinalですね。

 

<まとめ:隣の人に正しく説明できたらチェックを付けましょう>

□カプセル化とは、関連する情報と処理を1つにまとめること
 
□データ隠蔽とは、クラスのメンバ(フィールドやメソッド)の公開範囲をできるだけ限定すること
 
□「フィールドの値を更新するときには必ずメソッドを使って更新し、フィールドには直接アクセスできなくすること」もデータ隠蔽
 
□他のプログラマに設計者が意図しない操作をさせない、それがカプセル化の効果である
 
□Javaの基本的なクラス設計は、フィールドはprivate、メソッドはアクセサメソッドとして、publicにする

 

まとめができたら、アウトプットとして演習問題にチャレンジしましょう。

問題11.カプセル化

 

今回はカプセル化について見てきました。

カプセル化することでオブジェクトは堅牢になるのでした。

前回見た、継承とポリモーフィズム、そして今回のカプセル化の3つをあわせてオブジェクト指向の三大要素ということがあります。

次回のテーマは抽象クラスです。

抽象クラスはインスタンス化できないクラスです。

インスタンス化できないクラスにどんな存在意義があるというのでしょうか?

よくよく考えると今まで学んだポリモーフィズムと継承(オーバーライド)の中にそのヒントがあります。

推理しながら次回を楽しみに待っていてください。

 

JavaSE8の解説に戻る

 

【今回の復習Youtube】

020-オブジェクト指向の基本-カプセル化

PAGETOP
Copyright © Say Consulting Group, Inc. All Rights Reserved.