「オブジェクト指向プログラミングとは」と聞かれても、ぼんやりとしか分からない人が多いと思います。
それはオブジェクト指向が概念だからです。
プログラム言語はプログラミングを行うためのツールであり、コンピュータを動かすための言葉にすぎないので、あとは人間が考えてやらなくてはいけません。
そんな時に必要なのがオブジェクト指向という考え方なのです。
初心者のためにオブジェクト指向をまとめてみる
最初に、初心者の方のためにオブジェクト指向について、簡潔に説明します。
パーツの集まりがプログラムだとすれば、パーツに分けられるように作ること。その考え方がオブジェクト指向。
プログラムは作ったら終わりではなく、拡張性があるから柔軟に対応するため。個々が独立していることが望ましいため。
本当はこんなに簡単なわけではないのですが、非常に噛み砕くとこんな感じになるのではと思います。
オブジェクト指向を完全に理解することは無理!
オブジェクト指向を知るうえの心構えとして、完ぺきを求めるとよけいにだめになります。
底なし沼です。
1990年代にベストセラーとなったオブジェクト指向入門という本がありました。
「オブジェクト指向入門 原則・コンセプト」
「オブジェクト指向入門 方法論・実践」
この本、1冊あたり900ページあり、とても入門書とは言えない程のとてつもない良書です。
読み終えることができず倒れると思います。オブジェクト指向はプログラミングをやってない人にはさっぱり分からない仕組みなのです。
なので、一番簡単なのはプログラムを作ることです。
オブジェクト指向をある程度理解したうえでプログラミングをしていると、オブジェクト指向を考えるべきタイミングが何度も訪れます。
そこから深く考えていく方が理解が早く楽になります。まずはソースコードを書く第一歩を踏み出しましょう。
オブジェクト指向の要素
オブジェクト指向とはプログラミングを行う上での考え方というのは先ほども話しました。
その考え方の重要な要素の中に、
・継承
・カプセル化
・ポリモーフィズム
の3つがあります。
簡単に説明すると、継承とは、既存の機能を実現したプログラムコードを変更することなく、機能を継承してカスタマイズしたプログラムを書くことができる仕組みです。
カプセル化はアクセスできる変数をコントロールすることで、ポリモーフィズムとは関数名が同じでも、違うふるまいをさせられることです。
分かりにくいですよね。
実際にプログラミングでオブジェクト指向を理解する
オブジェクト指向はプログラムを「物」ととらえます。よくある例では車で想像してみるということをよくやります。
しかし、車とプログラムとでは、あまりにかけ離れすぎていて、私はよくわかりませんでした。
「設計書であるクラスからインスタンスとなるタイヤやエンジンを作成して車を作ります。」と言われても、たとえ話過ぎて「どれがエンジンなの?」ってなりました。
今思うとその理由は、考え方をソースコードレベルへ落とし込むことができなかったからだと思っています。
そこで、今回はオブジェクト指向の基礎となるクラスを「お財布」で説明します。
ソースコードレベルで理解したほうがわかりやすくなるので、PHPのソースコードを実際に動かしてみましょう。
paiza.ioというWEBでPHPを実行することができるサービスがあるので、これを使いながらやってみましょう。

プログラム言語はPHPを選択して、記載するコードをコピペして実行ボタンを押すと出力文字などを表示させることができます。
手を動かすことでより一層理解できるのでおすすめです。
クラスとインスタンスとプロパティとメソッド
では架空のお財布をプログラミングしてみましょう。
ここで覚えることは4つです。
クラス:設計図
インスタンス:設計図から出来上がったもの
プロパティ:クラスの中で使える箱のような入れ物
メソッド:クラスの中の関数
これを財布でたとえると、下のようになります。
クラス:お財布の設計図(紙に書いた設計図)
インスタンス:設計図から作られたお財布(存在するお財布のこと)
プロパティ:お金を入れる場所
メソッド:お金を入れたり出したいする処理
オブジェクト指向で設計図を作る
それではお財布の設計図を作ります。
お財布なので、お金を入れたり出したりできます。また、お財布を作成したときには中身は0になっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | <pre><code> // お財布クラス classWallet{ private$okane; function__construct(){ $this->okane=0; } functionnyuukin($zeni){ $this->okane+=$zeni; } functionsyukkin($zeni){ $this->okane-=$zeni; } functionikuraaru(){ echo$this->okane; echo"\n";// 見やすいように改行コードを出力します。 } } // お財布を作る $oreno_saihu=newWallet(); // 23,000円入金する $oreno_saihu->nyuukin(23000); // お財布へいくらあるか確認する $oreno_saihu->ikuraaru(); // 1,200円出金する $oreno_saihu->syukkin(1200); // お財布へいくらあるか確認する $oreno_saihu->ikuraaru(); </code></pre> |
※このソースコードはそのままpaiza.ioへコピペして実行できます。
[説明]
・クラスは「class クラス名{~}」で書かれます。上の場合はWalletがクラス名となります。自分で自由に決められます。
・財布にはお金が入るので、お金を入れるための変数$okaneを作りました。(private $okane の部分)この変数は「プロパティ」と言います。
・function __construct(){~}の部分はクラスからインスタンスが作成された時に実行されます。これをコンストラクターと言います。お財布の設計図から実際にお財布が出来上がった時には、まだお金は入っていませんので「0」を変数$okaneの中へ入れています。
・クラス内からプロパティへアクセスするときには$thisを使います。
・お財布へお金を入れるために function nyuukin($zeni){ ~ }を追加しました。このようにクラス内でfunction ~となっている箇所を「メソッド」と言います。入金用にnyuukin()メソッド、出金用にsyukkin()メソッド、残高確認用にikuraaru()メソッドを作りました。
オブジェクト指向で実際に作ってみる
これでお財布の設計図を作成することができました。しかし、まだ設計図の段階ですので、実際にお財布を作ります。
設計図からお財布を作ることを「インスタンスを作成する」と言います。
1 2 3 | <pre><code> $oreno_saihu=newWallet(); </code></pre> |
$oreno_saihu という お財布が実際に出来上がりました。
当然ながら残高は0円ですが、確認してみましょう。
残高確認用にikuraaru()メソッドを作っていましたね。このメソッドを実行してみます。
1 2 3 | <pre><code> $oreno_saihu->ikuraaru(); </code></pre> |
「0」と表示されましたね。
それでは、23,000円をお財布へ入れてみましょう。
入金用メソッドのnyuukin()へお金を入れるのですが、$zeniになる部分へ23000を渡します。
1 2 3 | <pre><code> $oreno_saihu->nyuukin(23000); </code></pre> |
これでお財布の中身は23,000円入っていると思います。落としたりしていないか確認してみましょう。
1 2 3 | <pre><code> $oreno_saihu->ikuraaru(); </code></pre> |
「23000」と表示されましたね。
ここで食事のために1,200円をお財布から出金しました。
1 2 3 | <pre><code> $oreno_saihu->syukkin(1200); </code></pre> |
残金はいくらになっているでしょうか?確認してみましょう。
1 2 3 | <pre><code> $oreno_saihu->ikuraaru(); </code></pre> |
「21800」と表示されます。
設計図であるクラス部分だけでは、PHPを実行しても何も起きません。
インスタンスであるお財布を実際に作ることで、使えるお財布を作成することができました。
ここまで出来ると基本的なクラスの動きは理解できたと思いますが、便利さを理解するには微妙だと思います。
しかし、同じ財布をもう一つ作るとどうなるでしょうか?
・設計図が一緒なので、衝突が起きて財布のお金が合わなくなってしまう?
・お財布自体は2つできているので、それぞれのお財布として整合性が合う?
オブジェクト指向で複数の財布を作ってみる
先ほどの財布は$oreno_saihuでした。今度は弟の財布である$otoutono_saihuも作ってみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <pre><code> $oreno_saihu=newWallet(); $otoutono_saihu=newWallet(); // 俺の財布へ20,000円入れる $oreno_saihu->nyuukin(20000); // 弟の財布へ5,000円入れる $otoutono_saihu->nyuukin(5000); // 俺の財布は今いくらでしょうか? $oreno_saihu->ikuraaru(); // 弟の財布は今いくらでしょうか? $otoutono_saihu->ikuraaru(); </code></pre> |
それぞれ、「20000」と「5000」が出力されたと思います。
勘の良い人はここで気づくと思いますが、処理をしているクラスは1つしかないのに、なぜ別々の値になっているのでしょうか?
これがクラスが設計図と言われる理由です。
クラスは処理をするためだけのプログラムでインスタンスをいくつも作ることができます。
今回の場合だとお財布の設計図を使って「俺の財布」と「弟の財布」を作ったので、それぞれのお財布として機能しました。
クラスの便利なところです。
継承して新しい機能を追加する。
お財布に入れるのはお金だけじゃない。
たしかに、お財布にはカードも入れたりしますよね。しかし、ここで問題が起きます。
兄「俺はシンプルな財布でいいからお金だけ使えるシンプルなのじゃなきゃいやだ。」
弟「俺はカードも入れられるようにしたい。お金だけ入れるなんてバカだ。」
喧嘩がおきそうですね。
お兄さんはカードは入れたくないみたいなので、お財布クラスである設計図を別々に作成しないといけないと考えますが、クラスには便利な機能があります。
それが「継承」という仕組みです。
継承とは既存のクラスの機能を使いつつ、新しい機能を追加することができる仕組みです。
お財布の設計図であるWalletクラスをベースにカードを入れられる機能を追加してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | <pre><code> // お財布クラス classWallet{ private$okane; function__construct(){ $this->okane=0; } functionnyuukin($zeni){ $this->okane+=$zeni; } functionsyukkin($zeni){ $this->okane-=$zeni; } functionikuraaru(){ echo$this->okane; echo"\n";// 見やすいように改行コードを出力します。 } } // カードが入れられる機能を追加したサブクラス classCardInWalletextendsWallet{ private$cards=array(); functioncard_in($card_name){ $this->cards[]=$card_name; } functioncard_out($card_name){ while(($ind=array_search($card_name,$this->cards,true))!==false){ unset($this->cards[$ind]); } } functioncard_check(){ echo"~ カード一覧 ~\n"; foreach($this->cardsas$card){ echo$card."\n"; } echo"~~~~~~~~\n"; } } </code></pre> |
お財布へカードを入れられる機能を追加しました。
クラス:Walletクラス
サブクラス:CardInWalletクラス
「CardInWalletクラス」は「Walletクラス」を継承していますので、Walletクラスのプロパティ、メソッドを使うことができます。
つまり、お財布へお金を入出金することができ、カードも入れられるようになったということです。
たとえば、俺はお金だけ入れられれば良いので、Walletクラスからインスタンスを作成します。
弟はカードも入れたいので、CardInWalletクラスからインスタンスを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <pre><code> // 兄の財布を作る $oreno_saihu=newWallet(); // 23,000円入金する $oreno_saihu->nyuukin(23000); // お財布へいくらあるか確認する $oreno_saihu->ikuraaru(); // 弟の財布を作る $otoutono_saihu=newCardInWallet(); // カードを入れる $otoutono_saihu->card_in('ポンティカード'); $otoutono_saihu->card_in('エコカカード'); $otoutono_saihu->card_in('クレカード'); $otoutono_saihu->card_in('遊戯王カード'); // カード一覧を見る $otoutono_saihu->card_check(); // カードを出す $otoutono_saihu->card_out('遊戯王カード'); // カード一覧を見る $otoutono_saihu->card_check(); // お財布へいくらあるか確認する $otoutono_saihu->ikuraaru(); </code></pre> この状態で兄の財布にカードを入れようとすると、カードの機能はないのでエラーになります。 <pre><code> $otoutono_saihu->card_in('ポンティカード'); </code></pre> |
基本となるプログラムが書かれているクラスを使って、機能を追加したり、機能を上書きすることも可能になります。
この仕組みが継承という仕組みです。
カプセル化してみる
クラスの中のプロパティを、どこからでも利用することができたら便利ですよね。でも、そういうことをさせないのがカプセル化です。
Walletクラスを眺めていると、お金が入っているところは、「$okane」の部分です。
上記で書いたクラスではクラスの外からは$okaneにアクセスできないよう「private」を付けています。
$okaneに数値を加算するときはnyuukin()メソッドを利用し、減算するときはsyukkin()メソッドを利用します。なぜこのようにするのでしょうか?
それは、入出金の時にお金じゃないものが入ってきたときに処理ができるようにするためです。
入金時は必ずnyuukin()メソッドを利用します。
現実的な財布の機能として、財布の中にノートパソコンを入れようとしても入れられません。大きさが足りなかったり、幅が足りなかったり、物理の法則に反してしまいます。
プログラムでは、データで表せるものはすべてプログラムとして扱えます。
$okaneに画像データを入れようと思えば入れられますし、音楽データも入れられます。
$okaneに入っていた数値と画像データを足し算することはできません。
そこで、$okaneには数値しか入れられないような処理をnyuukin()メソッドへ追加する必要があります。
たとえば、引数に数値以外のものが渡されてきた場合は、エラーを返すようにしたり、処理を進めないようにしたりします。
1 2 3 4 5 6 7 8 9 10 11 12 | <pre><code> functionnyuukin($zeni){ if(ctype_digit($zeni)){ // 数字だった時の処理 $this->okane+=$zeni; returntrue; }else{ // 数字じゃなかった時の処理 returnfalse; } } </code></pre> |
[説明]
ctype_digit関数で数字かどうかを調べます。
数字だったときは、$okaneに対して数値を加算し、処理が成功したことを伝えるために返り値をTRUEで渡します。
数字じゃなかった時は、加算処理はせず、FALSEを返します。
返り値を渡すことで、処理が成功したのか失敗したのかを知るフラグとなります。
このように直接利用されないように、$okaneに対して入口と出口を作り、入ってくるデータを制御することをカプセル化と言います。
複数人で開発を行う場合に、お金が入っているところを意識しなくても、nyuukin()メソッドを利用することで$okaneに数値を加算することができます。
$okaneに文字データなどを渡してプログラムがバグを起こさないようにするための仕組みとなるのです。
ポリモーフィズムを追加
プログラム的に言うと、同じメソッド名でも異なった処理を行える性質のことを言います。
前章までにお財布クラスを作ってきました。nyuuukin()メソッドを作って入金処理をするようにしましたが、入金は銀行口座でも同じような処理が可能です。
しかし、時間外手数料がかかったり、銀行口座がなければ入金できなかったりと同じ入金処理でも内部の仕組みが違ってきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | <pre><code> // お財布クラス classWallet{ private$okane; function__construct(){ $this->okane=0; } functionnyuukin($zeni){ $this->okane+=$zeni; } functionsyukkin($zeni){ $this->okane-=$zeni; } functionikuraaru(){ echo$this->okane; echo"\n";// 見やすいように改行コードを出力します。 } } // 銀行クラス classBank{ private$okane; private$tesuuryou; function__construct(){ $this->okane=0; $this->tesuuryou=100; } functionnyuukin($zeni){ if($zeni>100){ $this->okane+=($zeni-$this->tesuuryou); }else{ returnfalse; } } functionsyukkin($zeni){ if($zeni>100){ $this->okane-=($zeni-$this->tesuuryou); }else{ returnfalse; } } functionikuraaru(){ echo$this->okane; echo"\n";// 見やすいように改行コードを出力します。 } } // 俺の財布を作成 $oreno_saihu=newWallet(); // 俺の口座を作成 $oreno_kouza=newBank(); // 財布と口座に3,000円ずつ入金 $oreno_saihu->nyuukin(3000); $oreno_kouza->nyuukin(3000); // 財布と口座の残高を確認 $oreno_saihu->ikuraaru(); $oreno_kouza->ikuraaru(); </code></pre> |
$oreno_saihu->ikuraaru();を実行したときに3000と表示されたのに対して、
$oreno_kouza->ikuraaru();を実行したときには2900と表示されました。
これは、口座への入金時に100円が引かれているため、2900と表示されたのです。
同じ名前のnyuukin()メソッドですが、内部での処理が違うために多様性を持っているつくりとなっています。
※記載したコードについて
記載したコードについてはphpとして動くプログラムコードではありますが、機能として未完成です。
たとえば、今回作成したお財布クラスで作成した財布からはいくらでも出金が可能です。
夢のような財布ではありますが、実際のプログラムでこのようなことがあっては大変です。
このような部分を考えるのはSEなどのプログラムの設計を行う人ですが、どのように使われるのかを考えながらプログラムを作成することは重要になります。
オブジェクト指向だけにとらわれず、広い視野でプログラミングを楽しんでいきたいものですね。
オブジェクト指向・まとめ
オブジェクト指向は考え方であり、プログラムを書く上でのルールのようなものということが理解いただけたかなと思います。
一人で短期開発をするような小規模なプログラムの場合は、オブジェクト指向プログラミングをすることでソースコードが多くなってしまう場合もあります。
しかし、オブジェクト指向でプログラミングをすると、機能追加や機能変更などがしやすく、他の人が見てもわかりやすいコードになります。
保守性が高いコードはバグの修正も楽になになるのです。
また、昨今の開発手法では大規模で多機能なサービスを長期間かけてリリースする手法ではなく、小規模な機能でリリースをし、その後機能追加を行っていく方法が多く使われます。
リリース後の修正は結構面倒で、オブジェクト指向の考え方でプログラムを組んでいない場合、不要なところまで修正をしなければならず、思わぬバグを含んでしまうケースがあります。
なのでこれからのプログラミングではオブジェクト指向はとても大切なのです。
概念を知っていることと、作れることは大きく違いますからね。間違いを恐れずどんどんソースコードを書いていきましょう!