0 はじめに
0.1 おさらい
前回の記事では、オブジェクト指向以前のプログラミング言語の課題を挙げて、
- グローバル変数による保守性の悪さ
- サブルーチンしか共通部品を作れないという再利用性の低さ
を克服するために生み出されたのがオブジェクト指向であることを確認しました。
そしてオブジェクト指向のコンセプトは「プログラムの無駄を省いて整理整頓すること」です。
この課題をどのようにしてオブジェクト指向が解決していったのか、これから2回にわけて私の理解をまとめていきたいと思います。
諸事情によって付番が狂ってしまいまして申し訳ありません。
0.2 本稿の目的
本稿の目的は、私と同じような初学者諸氏に、オブジェクト指向でコードは「まだ書けない」けど「意図が理解はできる」ようになってもらうことです。
そして、オブジェクト指向がわかっている前提で書かれている教材をつまづかずに読めるようになることを目的としています。
オブジェクト指向はわかってるから、どう書いたらいいのか「how」が知りたいという方は想定しておりません。
オブジェクト指向ってふわふわする
私がオブジェクト指向を勉強し始めた時の感想です。
このとき見た資料では、大抵コードがズラズラっと書かれていて、「こう書くよ」という解説がなされており、オブジェクト指向が「何者」かについては、「モノ中心主義です」みたいなふんわりした説明で済まされていることが多かったです。
しかしそのコードが知らない言語のものだったり、あるいは知らない文法が使われていたりすると、初心者の私には理解が難しく、結局その「書き方」がオブジェクト指向プログラミングらしい、とは思うものの、何を目的としたどのような概念なのかは理解できませんでした。
単にコードのパターンを1つ暗記しただけ、というような結果になってしまいました。
まずはオブジェクト指向で思考したい!
このような問題意識から、本シリーズではコードを使わずに「日本語で」オブジェクト指向の概念について説明していこうと思います。
(もちろん数学と同じで、中級者以上の方には日本語より、数式やプログラムで書いた方がわかりやすいのだと思いますが初学者の工夫にお付き合いください汗)
コンテンツは**「クラス」、「承継」、「ポリモーフィズム」**に限定します。いわゆる「三本柱」というものです。
基本的には「オブジェクト指向でなぜつくるのか」の内容を中心にしております。
今後のプログラミング学習に役だっていただければ幸いです。
※内容間違いなどあればご指摘ください。この記事は、筆者の理解を自分の言葉で表した「素人の見解」です。
1 クラス
1.1 クラスは「まとめて、隠して、たくさん作る」仕組み
本書ではクラスの要素を「まとめる」、「隠す」、「たくさん作る(製造機)」と評しています。
また、クラスとインスタンスの関係について、たい焼き製造機とたい焼きのような関係と書いています。
要素ごとに説明していきます。
1.2 「まとめる」
オブジェクト指向の使命の1つは「グローバル変数からの脱却」です。
グローバル変数を変更するたびに、関係ないコードまで全てをチェックしなくてはなりませんでした。
また、新時代のアプリケーションでは、ハードウェアの機能が進化するに応じて、膨大な数の変数とサブルーチン(関数)を有するよう大規模化が進んでおり、それらを整理整頓して見やすくメンテナンスしやすくすることも求められていました。
それを解決するために「クラス」が作られました。
インスタンス変数は、クラス専用グローバル変数
クラスとは、結びつきの強いグローバル変数とサブルーチン(これをメソッドと呼びます)をまとめたものです。
そして「クラス」専用のグローバル変数のことをインスタンス変数と呼びます。
インスタンス変数はクラスの外からはアクセスできないように隠すことができるので、想定していない場所からの悪影響を回避できます。
特定のグローバル変数に関係するサブルーチンだけをまとめてしまえば、その変数を変更した時には、まとめたメソッドだけをチェックすれば十分なので、保守性がだいぶ高まります。
しかも連関する変数とメソッドをひとまとまりにできるため、見た目にアプリケーションの部品数が減りました。
例えばこれまではバラバラに存在していた「ファイルを開く」、「ファイルを読み込む」、「ファイルを閉じる」という別々のサブルーチンも、「テキストファイルリーダー」というクラスにまとめてしまえば、「ファイルNoという変数に与えられた番号のファイルを、開いて読み込んで閉じられる機能をまとめたクラス」とかたまりにして扱えるようになったのです。
このようにして「関係性」で変数やサブルーチンをまとめることで、「探しやすくなる」のもメリットです。
例えば、パソコン内のファイル全てをルートディレクトリに並べたと思うと、特定のファイルを探し出すのは至難の業です。
ですが、それをジャンルごとに「音楽」、「写真」、「仕事」・・・と整理すれば、使いたいファイルをすぐ見つけられるはずです。
これらが「まとめる」効用です。
1.3 「隠す」
これもグローバル変数対策です。
まとめることで、クラス内部においてインスタンス変数の変更がどのメソッドに影響するかは限定できます。
しかし、依然としてクラスの外側から意図せずインスタンス変数が変更されたり、他のプログラムに影響を与えてしまう恐れがあります。
意図せず関係ない場所で使われた場合、どんな悪影響を及ぼすかもわかりません。
これを防ぐために、クラス内の変数やメソッドにはクラスの中でしか使えないように隠す機能があります。
もちろん、全ての変数が隠されるわけではなく、明示的に隠すことを宣言することになります。
これによって、グローバル変数を完全に使わずにプログラムを組むことができるようになります。
1.4 「たくさん作る」
これが「クラス」においてもっともユニークな機能です。
いわゆるインスタンス変数を作る「製造機」としての機能です。
例えば先ほどのファイルリーダークラスの例で考えてみます。
これは「アクセスしているファイル」を「開けて読み込んで閉じる」機能のかたまりです。
この「アクセスしているファイルを保持する変数」は1つしか定義しません。
しかし従来の技術では、この変数を配列にでもして複数設定しなければ複数のファイルを「同時に」開いて読んで閉じることはできませんでした。
しかし、「クラス」においては、変数(入れ物)が1つでも複数のファイルを指定してメソッドを同時実行できるのです。
その秘密が「インスタンス」にあります。
インスタンスはメモリ領域
少しでもオブジェクト指向プログラミングをしたことがある人は、
$reader1 = new TextFIleReader();
$reader2 = new TextFIleReader();
みたいなことを書いたことがあると思います(コード使ってしまって申し訳ないです)。
私はこれを「インスタンス変数を作る」と習いました。
しかし、インスタンス変数とは何者か、そしてインスタンスとはなにが違うのか、わかりませんでした。
ちなみにこれ、正しくは「インスタンスを作り、変数に代入している」ということです。
基本のきですが、私は最初、プログラミング言語の「=」も等号だと思っていたのです。
実際には、右辺を左辺に「代入」する比較演算子ですね。
では「インスタンス」とはなんでしょうか。
これは、「クラスで定義されたインスタンス変数を格納できる容量のメモリ領域」のことです。
このインスタンス = 確保されたメモリ領域ごとに異なる値を保持できる変数を「インスタンス変数」と呼んでいます。
そろそろややこしくなってきたと思うので詳しく説明します。
まず、「クラス」内ではインスタンス変数が定義されています。
次に、アプリケーションを実行してインスタンス変数を生成(いわゆるnew)すると、定義された変数を格納するのに必要な分の領域をメモリ上に確保します。
これがインスタンスです。
最後にインスタンスを変数に格納するのですが、メモリ領域を変数に格納するとはどうやっているのでしょうか?
答えとしては、そのインスタンスが確保している「メモリ上の場所(ポインタ)」を変数に格納しています。
つまり、住所のような「メモリ上の位置情報」が変数に入っています。
よってインスタンスをnewするたびに、事前に複数の変数を定義しておかなくても、メモリ上に領域を確保してその場所を変数に渡すことで、異なるインスタンスをいくつも同時に作成できるのです。
このように、オブジェクト指向型のプログラミングでは「同種の情報を複数同時に扱う処理」であっても変数を1つ定義しておけばいくつでもインスタンスを作成できるので、ロジックがシンプルになります。
※ただし、クラス内にインスタンスがたくさんあると、メソッドを実行する際にどのインスタンスで実行するのかわからないので、処理対象のインスタンスを指定してメソッドを実行することになります。
1.5 クラスまとめ
クラスは「まとめて、隠して、たくさん作る」ための仕組み
インスタンス変数は「クラス内のグローバル変数」として設定できる
インスタンスは「クラスで定義されたインスタンス変数を確保するためのメモリ領域」のこと。
クラスをnewするとメモリ領域が確保されて、指定した変数にそのメモリ領域の住所が代入される。
1つの変数の定義から、異なるメモリ領域を何個も作って住所を変数に渡せば、いくつでもインスタンス変数を作成できる。