前回まで、プログラミングの共通する考え方を説明してきた。
しかし、ここからはちょっと個別の内容が増えてしまうので、言語で分けていく。
まずはJavaだ。オブジェクトに関してはかなり強い言語なので、最初の説明にはもってこいだろう。
難しい話も出てくるが、極力簡単に説明するつもりだ。
あと、今回はほぼお話だけ。具体的なソースコード等は次回から順次出していく。
今回の説明内容
今回から言語別の説明に入る。
まずはJavaのオブジェクトを深堀りしていこう。
とはいえ、かなり量が多いので、複数回に渡って解説する予定だ。
今回は前回の内容も絡めながら、オブジェクトの基礎をもうちょっと詳しく解説していく。
具体的には、考え方の基になっている3つのキーワードを解説していこう。
オブジェクト
復習:オブジェクトとは
軽く復習しておこう。
オブジェクトとは、複数のデータや処理を一つのまとまりにしたモノだ。
詳しくは前回の内容を見返してほしい。
プログラミング考え方講座「オブジェクト・構造体」 | Shino’s Mind Archive
例えば、前回出した例だと、本というオブジェクトを考えることができる。
持つデータは以下の5つ。
- 本のタイトル
- 著者
- 出版年月日
- 版数
- 値段
そして、以下2つの処理を持たせた。
- 版数を上げる
- 情報を出力する
これらを、一つにまとめ、Book
というクラスを作っていた。
なぜオブジェクトを考えるのか
これなのだが、前回解説したような内容だけだと、まだ実感が湧きづらい。
どういうことかというと、データをまとめたところで利点が少ないのだ。
あ、ないわけではない。
各データを一定の形にすることができるとか、共通の処理が定義できるとか、それらも十分利点になる。
しかし、まだ現段階では扱うデータ数が少ないので、これらの恩恵を十分には得られないのだ。
というわけで、実感が得られやすいように基本的な3つの考え方をご紹介しよう。
以下の3つだ。
- カプセル化
- 継承
- 多態性(ポリモーフィズム)
一つずつ見ていく。
カプセル化
まず言葉だけで説明すると、カプセル化とは必要最小限の情報のみ公開し、情報を内部に隠蔽しようという考え方だ。
例えば、自分の銀行口座を思い浮かべてみよう。
銀行口座があり、毎月その中に一定の金額が会社から振り込まれる。
で、クレジットの支払いがあれば逆に一定の金額が出ていく。
外部から見ると、とりあえず口座があるというのだけ分かっており、その内部の情報はこちらから伝えなければわからない。
簡単に言ってしまえば、これがカプセル化だ。
まず、銀行口座は他人に情報を公開するようなものではないだろう。
ただ、会社には銀行名や支店名、口座の名義、口座番号を事前に伝えているはずだ。
それらの情報と振込額を受け取って、一致したらお金を受け取るような処理を書けるだろう。
また、逆にクレジット会社にも同じように伝えているはず。
こちらも、要求があれば条件を見て、問題なければ支払う処理が書ける。
これらの処理を、疑似的にちょっと書いてみよう。
class Kouza {
private int bank_code; // 銀行コード
private int shop_code; // 支店コード
private int kouza_code; // 口座番号
private String name; // 口座の名義
private int pass_code; // パスワード
private int remain; // 残高
public Kouza() {
// 初期化
}
public int inMoney(){
// 銀行コード~口座番号までを受け取り、
// 合っていたら入金を受け付ける
}
public int outMoney(){
// 銀行コード~口座番号までを受け取り、
// 合っていたら出金を受け付ける
}
}
前回解説したものよりちょっと記述が増えていることにお気づきだろうか。
変数の前にprivate
とついており、コンストラクタやメソッドの前にはpublic
とついている。
これらはアクセス修飾子といって、これらの情報がどの範囲からアクセスできるかを表している。
他にもう2種類あるが、とりあえずprivate
はそのクラス自身内でしかアクセスできず、public
はどこからでもアクセス可能だと思ってほしい。
そうすると、このKouza
クラスには、外から見たらどんな情報があるのかすら分からない。
ただ、新しく作ることと、入金、出金の受け口があることだけはわかる。
これが、必要最小限の情報公開、カプセル化というものだ。
ちなみに、よくget○○()
とかset○○()
とかで全てのprivate
をつけた変数にアクセスしているようなサンプルを見かけるが、あれはカプセル化ではないと個人的に思っている。
折角private
で隠しているのに、メソッドを介して全てアクセスできてしまうので意味がない…
継承
継承とは何か、こちらも先に言葉で説明してしまおう。
これは、ある共通した事項を定義したクラスの情報を受け継いで、新しいクラスを作ることができる、というものだ。
いつものごとく例を見ていこう。
まず、最初にゲーム機というクラスを作ったとしよう。
これに考えられる情報や処理としては、ゲームソフトを入れられる、起動して遊ぶことができる、セーブができる辺りにしておこう。
ということは、まずゲーム機のクラスは以下のようになる。
あ、ゲームを表すGameSoft
クラスは別で定義済みとしておこう。
class GameHard {
GameSoft soft; // ゲームソフト
void playGame(){
// softに設定されているゲームで遊ぶ
}
void saveGame(){
// セーブする
}
}
まずはこんな感じ。
さて、実際のゲームを見ていこう。ちょっと古いが、Nintendo 64とゲームキューブにしよう。
これで、セーブするときを考える。
Nintendo 64の場合は、ソフト内に記憶する部分があり、そこにセーブする。
それに対し、ゲームキューブではメモリーカードを別途差し込んでおき、それにセーブする。
というわけで、セーブの処理が変わるのだ。
しかし、他のゲームをする部分については共通している。
そこで、共通の定義を行っていたGameHard
クラスを引き継ぐ、ということになる。
そのまま保持する情報や機能を引き継ぐので、引き継いだ先の両ハードともソフトを差し込み、ゲームを遊ぶことができる。
それ以外の、セーブする部分を個別で定義し直してあげれば、それで完成なのだ。
これが、継承というものだ。
疑似的なコードを以下に書いてみよう。
上で書いたGameHard
クラスは定義済みとしておいて欲しい。
class Nintendo64 extends GameHard {
void saveGame(){
// ソフト内にセーブする
}
}
class GameCube extends GameHard {
MemoryCard mc;
void setMemoryCard(){
// メモリーカードをセットする
}
void saveGame(){
// メモリーカードがセットされているか確認する
// メモリーカードがセットされていれば、その中にセーブする
}
}
class MemoryCard {
String data;
}
…メモリーカードのクラスは適当だが許してほしい。
とにかく、このように継承を使うと一つの基準となるモノから、処理を追加したり、書き換えたりすることで、機能や情報を拡張することができる。
多態性(ポリモーフィズム)
最後にこの多態性だ。
これは、同じ書き方で、それぞれのモノに合った処理をしてもらおうという考え方。
実は、継承の部分で一つ使っている。
ゲームの例で、遊ぶまでは同じだったが、セーブの部分については処理が変わっていた。
しかし、呼び出しの場合はsaveGame();
で共通して呼び出せる。
これがNintendo 64ならソフト内にセーブすることになるし、ゲームキューブならメモリーカードにセーブすることになる。
このように違う処理でも同じ呼び出し方で使えることが、多態性という考え方になる。
まとめ:Javaにおけるオブジェクトの3つの考え方
今回は、ひたすら言葉の説明をしてきた。
オブジェクトを使うときに何を考えるのか、という3つの指標だ。
カプセル化は、必要最小限の情報のみ公開すること。
継承は、基となる情報や機能を定義しておき、それを受け継いで新しく処理等を追加すること。
そして、多態性は異なるモノでも、外から見たら同じ方法で呼び出せること。
これらはちょっと難しい考え方かもしれないが、意識しておくと組みやすくなるだろう。
次回は、カプセル化に出てきたアクセス修飾子というやつを詳細に見ていくとしよう。
その他、必要なパッケージという考え方にも触れる。
更新情報はTwitterでも呟いている。よかったら、ページ下部のTwitterアイコンから覗いていってほしい。
それでは。
コメント