私はRuby on Rails から学び始め、現在はJavaScriptについて学んでいる。「オブジェクト指向」は掴み難い難解な概念だと思っていたし、今も完全に理解できたわけではないが、学んだことを自分なりにまとめておこうと思う。
クラス、オブジェクト、インスタンス、インスタンス変数
クラスとは、「オブジェクトの元になるもの」である。では、オブジェクトとは?
オブジェクトとは、「クラスを新しく生成したときにできるモノ」である。(オブジェクト指向においてはオブジェクト=インスタンス。なのでクラスを新しく生成するとインスタンスができる、と言い換えて良い。)
例えば、たい焼き屋を考えてみる。たい焼き屋では、(当たり前だが)たい焼きを売っている。そのたい焼きはどうやって作られているのかというと、「たい焼きの型」に小麦粉を溶かした液を注いで、あんこを注入して、ひっくり返して・・・という工程を経て、たい焼きが作られる。この「たい焼きの型」がクラスである。つまり、型だけでは食べることができないけれど、一度たい焼きの型を作ると、同じようなものなら何個も作ることができる。一方で、作られた「たい焼き」がオブジェクト(インスタンス)である。このたい焼きは同じような形をしているが、たい焼きの中身は好きに変えていい。粒あんにしてもいいし、クリームにしてもいい。このように、「たい焼きの形はそのままに、中身を変える」ものがインスタンス変数である。
さて、ここでもう少し具体的な話になるが、例えば、「人」というクラスを作ったとする。
const person = class{}
このクラスには、何も入っていない。空っぽである。
そこで、『「人」といってもいろんな国の人がいるし、身長や体重も違うよね』と考え、「人」クラスには国籍、身長、体重の3つを加えることにした。そうすると次のように書く。
const person = class{
constructor(nationality, height,weight) {
this.nationality = nationality;
this.height = height;
this.weight = weight;
}
}
ここで、constructorとは、「初期化強制処理」を表している。
初期化強制処理とは、「変数(nationality, height,weight)の初期値を決め、かつこれらの変数はオブジェクトを作る際に必ず必要ですよ」という意味である。
この場合で言うと、
this.nationality = nationality;
は、this.nationalityがJavaScriptでいうインスタンス変数を表しているから、「nationalityというインスタンス変数にnationalityをひとまず入れる(初期値)」ということである。
件のたい焼き屋の話に戻すと、たい焼きの中身はインスタンス変数で決めることができると書いたが、「インスタンス変数の初期値はあんこ、オブジェクトを作る際はクリームに変えていいよ」ということになる。
なぜconstructorが必要か
① コンピュータサイドから考えてみよう。(かなりイメージ的です)
上の図は、メモリとCPUの関係を表している。メモリには番地(アドレス)があり、CPUとデータのやり取りをしている。
ここで、メモリの中に「人」クラスを表すアドレスを確保し、さらに、「人」クラスから「国籍」「身長」「体重」を表すアドレスを確保したとする。
このとき、もし「国籍」や「身長」や「体重」に初期値が入っていなかったらどうなるだろうか?「国籍」「身長」「体重」を表す場所は確保したけれど、中に何もない。けれども、メモリとしては何らかの数値が入っていないといけないのだ。したがって、「初期値が入っていない=不定形」となり、そうなるよりも、あらかじめ初期値となるものをいれておいて、後から書き直せるようにようにした方が良いのである。
② プログラマーサイドから考えてみよう
プログラマーから見た場合、
const person = class{
constructor(nationality, height,weight) {
this.nationality = nationality;
this.height = height;
this.weight = weight;
}
}
と書いた場合、
const takumi = new Person(japan,180,65); //数値は理想です
console.log(takumi.height); //=> 180
console.log(takumi.weight); //=> 65
このように新しいインスタンスを作成した時点で、3つの引数を渡してやることで、「書き漏れ」がなくなるのだ。
懲りずにたい焼き屋で考えると、 constructorで初期値を強制的にセットしてやらないと、オブジェクトを生成した際にあんこの入れ忘れが起きてしまう可能性があるのだ。
10人お客さんがきて、7人にはあんこを入れたたい焼きを売ることができたが、残りの3人のたい焼きにはあんこを入れ忘れていた、なんてことが起きたら大変である。
したがって、constructor(初期値強制化)することは①②両サイドにとって意義深いと考える。
汎化、特化、クラスの継承、オブジェクト指向
例えば、これからお祭りの準備をすると考える。お祭りの準備といってもさまざまなカテゴリーがあるが、今回は「小麦粉を使ったお菓子」のお店の運営を私がマネジメントすることになったとする。
私は『「小麦粉を使ったお菓子」といえば「たい焼き」と「カステラ」だな』と考え、2つの店を出店することにした。
こうして、「小麦粉を使ったお菓子クラス」から、「たい焼きクラス」と「カステラクラス」が誕生した。「たい焼きクラス」と「カステラクラス」は共に「小麦粉を使ったお菓子」だけれども、違うモノである。このように、あるクラスからさらに細分化していくことを「特化」という。
そうこうしているうちに、私のマネジメント力が買われて、今度は「小麦粉料理」のお店の運営を任されたとする。
すると今度は「小麦粉料理」全般について視点を広げてみる必要がある。このように、あるクラスよりも大きなクラスをつくることを「汎化」という。
親となるクラスから子クラスへと基本となる情報を引き継ぐことを「継承」という。
オブジェクト指向というのは、世の中のいろいろな「モノ」をオブジェクトとして捉え、オブジェクトのもつ共通性や、違いを元に関係性を定義することではないか、と思う。
まだまだわからないことはたくさんあるし、曖昧な点もたくさんあるが、ひとまずまとめておく。
間違いがあれば指摘してくださると嬉しいです。