手続き型言語をやっていると、データを組み合わせて取り扱う必要が出てくる。
例えば、顧客のデータを扱う必要があるとき、顧客の「名前、住所、所属、電話番号、取引内容...」などをまとめて取扱いたい。
そこで、構造体という発想が出てくる。
構造体を扱っていると、新しく顧客データを作るとき、毎回毎回、作った後に同じ動作をしないといけないことに気づく。
そしたら、それをいっぺんにやってしまうために関数を作ることになるだろう。
init_customer(struct Customer*, char* name, char* addr, char* tel)
また、逆に顧客データが不要になったとき、メモリ解放などをさせるために、
delete_customer(struct Customer*)
も作ることになるだろう。
さらに、取引を行うたびに、取引データを追加しないといけない。そのために、このような関数を作るだろう。
add_deal_customer(struct Customer*, char* deal_name, char* deal_ammount)
そして、複数人でプログラムを作っていると
「おいおい!せっかく取引データ追加用のadd_deal_customer作ったのに、なんで自分で勝手に追加してるんだよ!てか、その方法だとメモリ解放どうすんの?ちゃんと作ったの使ってくれたら、delete_customer()でできるようになってるのに」
って状況が生じうる。それを防止するために、コメントに
「/* 取引データの追加は必ずadd_deal_customerを使うこと! */」
と書くことになるだろう。
てか、わざわざコメント書いたのにこいつ読んでねーし。あーあ、めんどくさいめんどくさい。
そこで、だ。言語を少し別のものにかえて、構造体に関数を持たせられるようにしよう。
そして、構造体ができたときに、自動でinitって関数を、削除されるときに、自動でdelって関数を呼ぶとしよう。
そして、add_deal_customerも構造体の中に入れてしまおう。名前は長くて面倒なので、
Customer.add_deal(char* deal_name, char* deal_ammount)
のようにしてしまおう。
さらに、add_dealを使わずに直接、取引データを追加しやがるならず者対策も付け加えてしまおう。
privateにした変数は、構造体が持ってる関数からしか、いじくれないようにしてやろう。
今まではコメント読まない馬鹿の世話に苦労していたのだが、これからはコンパイラがそういうやつにエラーを出してくれる。
次は継承のお話。話は変わって、今度はGUIパーツで説明する。
ボタンってあるよね。あれを作りたい。
普通に文字が書いてあって押したら何かが起こるボタン、アイコンが描いてあって押したら何かが起こるボタン、
この2つを作りたい。
まず、文字のボタンを作ろう。
関数を持てる構造体を作り、「表示する文字(変数)、クリックしたときの動作を定義する関数へのポインタ(変数)、描画命令が出た時に描画する関数(関数)、クリック命令を受け取り、構造体にセットされた関数へのポインタを呼び出す関数(関数)」
がいるかな。
次に、アイコンのボタン。「表示するアイコン(変数)、クリックしたときの動作を定義する関数へのポインタ(変数)、描画命令が出た時に描画する関数(関数)、クリック命令を受け取り、構造体にセットされた関数へのポインタを呼び出す関数(関数)」
あ、さっき作ったのとほぼ同じじゃーん!文字ボタンの構造体をコピペしちゃえ!
あとは文字の変数を、アイコンに変えて、描画命令を、文字描画からアイコン描画に変えればできるじゃん!
ところが、文字ボタンのクリック命令を受け取る関数にバグが見つかった!よし、デバッグできた!
けど、アイコンのボタンにコピペしてたんだった!またコピペしなおしじゃん。あーめんどくさ。
もしそれ以外に、チェックボックスのボタンとか、もっと別のボタンとか、いろいろコピペで作ってたらもっとめんどくさ。
そこで、こんなことができたらいいんじゃないか?
文字ボタンにも、アイコンのボタンにも共通する、関数をもった構造体を作っておく。
ボタンの構造体「クリックしたときの動作を定義する関数へのポインタ(変数)、クリック命令を受け取り、構造体にセットされた関数へのポインタを呼び出す関数(関数)」
そして、文字ボタンは、ボタンの構造体に「表示する文字(変数)、描画命令が出た時に描画する関数(関数)」を、
アイコンのボタンは、ボタンの構造体に「表示するアイコン(変数)、描画命令が出た時に描画する関数(関数)」を追加すればいいんだ。
これを、継承と呼ぶ。
さらに、文字ボタンもアイコンのボタンも、同じ「ボタンの構造体」を持っているから、どっちも同じ「ボタン」として扱うことができる。
後で画面内のボタンを全部解放する必要があるときとか、実はこれ、すごく便利。
別の構造体で扱っていたら、文字ボタン用の配列とアイコンボタン用の配列を用意し、それぞれに作ったボタンをいれ、解放するときはそれぞれの配列の中身を解放しないといけない。めんどくさ。
けど、どっちもボタンを継承しているから、ボタンの配列を用意して、文字ボタンもアイコンボタンも全部同じ配列に入れて、1つの配列の中身を解放したらいい。楽ちん。
はい、そうすると、関数を持った構造体はもはや、もとの構造体とは違う感じになりました。
これをクラスと呼びましょう。クラスから作られたデータは、オブジェクトと呼びましょう。
なんか、オブジェクトがほかのオブジェクトから独立してカプセル化されてるみたいですね。
そして、オブジェクトが持ってる関数。メソッドとでも呼びましょうか。これ、まるでオブジェクトが自分のやりたいことを知っているかのようです。
手続きを構造体や関数に細かく分離していくにつれて、構造体と関数の組み合わせってのが出てきて、まとまりが見えてくるのです。
そいつらをまとめてしまって、一つの部品として扱って、外からはあんまり部品の中身を見えないようにしましょう。
部品の中身を直接いじらせるんじゃなくて、部品をいじらせるための関数を用意して、それを経由してやってもらいましょう。
それが、カプセル化とかいうやつです。
ぶっちゃけ、アンサイクロペディアの解説 http://ja.uncyclopedia.info/wiki/%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91 で十分以上にわかりやすいと思うんだ。
自分的には 構造体を覚える 構造体に構造体を入れ始める 構造体にあらゆるものを詰め込みたくなる 構造体にフラグとvoid*を入れ始める 関数がフラグ判定だらけになり始める 関数...