クラスから理解するPHPのオブジェクト指向
エンジニアになろう!
PHPでのオブジェクト指向とは?
PHPの開発でよく耳にする「オブジェクト指向」。
このオブジェクト指向の意味が分かっていない状況では、PHPのプログラミングの理解も進みません。
そのため、まずは、「PHPでなぜオブジェクト指向が必要とされているのか?」また「オブジェクト指向を理解するにあたり何から理解すると良いのか?」から解説していきたいと思います。
こちらの記事もオススメ!
2020.08.14PHP 特集知識編PHPおすすめフレームワーク5選PHPでコードを書くならオススメの統合開発環境「PhpStorm」クラスから理解...
2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...
オブジェクト指向で最初に覚えるべき4つのキーワード
オブジェクト指向を理解するにあたり、最初に覚えるべき4つのキーワードがあります。
- クラス
- メソッド
- プロパティ
- インスタンス
オブジェクト指向には、色々なキーワードが出てきますが、PHPでオブジェクト指向を理解するにあたり最初は、この4つで十分です。
クラスを中心とした概念で構成される
オブジェクト指向は、クラスと呼ばれる概念で構成される考え方です。
PHPで記述したクラスを作成し、それを用いてオブジェクト(=モノ)を作成するプログラミング法です。
クラスについて例えると、「設計書」とよく言われています。
クラス
クラスは、オブジェクトを作るためのテンプレートとなるものです。
クラスの中には、変数や関数などが含まれています。
テンプレートであるクラスを利用して、後ほどご説明するオブジェクトを作成していきます。
(ガンダムを例に)クラスを作ってみよう!
今回は、メディア編集部の独断で、「ガンダム」を例に説明していきます(笑)
ガンダム(モビルスーツ)には多くの種類がありますよね。
例えば「ザクII」や「デスティニーガンダム」などたくさんいます。
それぞれのガンダムを1体1体実装するのも良いですが、これはスマートではありません。
そこで、共通して最低限必要な持つ機能や特性、例えば機能は「歩く」「しゃがむ」、特性は「名前」「大きさ」「重量」などは、まとめてしまいましょう。
これがクラスの考え方です。
言葉だけだとわかりづらいので、早速ですが、シンプルなガンダムクラスを作ってみましょう!
クラス名 | ガンダム (Gundom) |
共通して持つ特徴 | 名前、全長、重量、色 |
共通して持つ機能 | 歩く、しゃがむ、攻撃する |
かなりシンプルですが、このようにまとめることができます。
このとき、「共通して持つ特徴」と例えている部分を、メンバ変数(またはプロパティ、インスタンス変数)と呼びます。
「共通して持つ機能」と例えている部分を、メンバ関数(またはメソッド)と呼びます。
実際にコードで書いてみると
1<?php
2
3class Gundom
4{
5 // メンバ変数(プロパティ、インスタンス変数)
6 private $name;
7 private $height;
8 private $weight;
9 private $color;
10
11 // メンバ関数(メソッド)
12 public function walk(){} // 歩く
13 public function squat(){} // しゃがむ
14 public function attack(){} // 攻撃する
15
16
17}
のように書けます。
private とpublic はオブジェクト指向にとって重要な要素です。
意味は後ほど説明します。
メソッド
クラスの中で定義された「関数」のことをメソッドと呼びます。
上記コードで言うと、ここの部分です。
1// メンバ関数(メソッド)
2public function walk(){} // 歩く
3public function squat(){} // しゃがむ
4public function attack(){} // 攻撃する
いわゆる作ったモノ(オブジェクト)に対する命令文です。
基本的な動きは、PHPで定義されるその他の関数と同じであると理解して問題ありません。
メソッドは関数ですので、その役割は{ } の中に何かしらの行動を定義することができます。
メソッドが存在することで引数によって計算結果が変わるなど同じクラスから生まれたものでも、異なった結果を生み出せるようになります。
また、public が付いているのは、クラス外からこれらの関数を呼び出せる、というのを示しています。
つまり、外側からクラスを扱いたいもの(命令したいもの)にpublic をつけます。
プロパティ
クラスの中で定義された「変数」のことをプロパティと呼びます。
上記コードで言うと、この部分です。
1// メンバ変数(プロパティ、インスタンス変数)
2private $name;
3private $height;
4private $weight;
5private $color;
クラスの中で定義されている場合のみ、プロパティと独自の名前がついています。
変数ですので、基本的には皆さんがイメージするとおりで、クラスの中で動的に変更される数値などを扱います。
プロパティがあることで、クラス内で様々な変数を管理できます。
先ほどのメソッドと違い、プロパティには基本的にprivate をつけます。
これは、クラス内でしか操作できないもの、を指します。
つまり、外側からはこれらの変数に触ることはできません。
今の段階ではイメージがつきにくいかもしれませんが、このまま読み進めていくと理解できると思います。
インスタンス
設計図にあたるクラスを利用して、実際に「オブジェクト」を作成したものをインスタンスと呼びます。
インスタンスを作成することで、具体的にPHPで様々な作業をオブジェクトに任せられるようになります。
コンストラクタ
インスタンスを作成する方法を学ぶ前に、コンストラクタについて理解しておく必要があります。
コンストラクタとは、インスタンスが生成されたときに一番最初に呼び出される関数です。
PHPではfunction __construct(){} で定義します。
このとき、必要な引数を定義して、メンバ変数(プロパティ)を初期化したりします。
先ほど作ったガンダムクラスに追加するならば、
1// コンストラクタ
2function __construct($name, $height, $weight, $color)
3{
4 // メンバ変数を初期化
5 $this->name = $name;
6 $this->height = $height;
7 $this->weight = $weight;
8 $this->color = $color;
9}
のように書くのが望ましいです。
メソッド内で、自身のクラスのメンバ変数を扱う時は$this ポインタをつけてアクセスします。(重要!)
オブジェクト(インスタンス)の作成方法
具体的にオブジェクトを作成する方法を確認していきましょう。
オブジェクトを作成する場合、以下のように新しいオブジェクトを作成します。
1$zaku_ii = new Gundom("ZakuII", 17.5, 56.2, "Green");
これで、ガンダムクラスのインスタンス$zaku_ii が作成されました。
このように、インスタンス生成時にコンストラクタの引数を与えます。
コンストラクタを定義しない場合や引数を取らない場合は、
1$zaku_ii = new Gundom;
2$zaku_ii = new Gundom();
のように書けます。
他のオブジェクトも作成してみる
今、作ったガンダムクラスをもとに別のガンダムを生成することもできます。
1$destiny_gundom = new Gundom("Destiny Gundom", 18.08, 79.44, "Blue & White");
これで、別のガンダムが生成されました。
このように、同じクラスをもとに別の特徴と同じ機能を持ったモノ(オブジェクト)が生成されます。
これがオブジェクト指向の基本的な考え方なのです。
作成したオブジェクトに命令させる
最初に、メソッドをいくつか作りました。
それらは、オブジェクト(インスタンス)が生成されて初めて、以下のように扱うことができます。
1$zaku_ii->attack();
2$zaku_ii->walk();
3$zaku_ii->squat();
もちろん何も内容を定義していないので何も起こりません。笑
メンバ変数(プロパティ)を外部から参照したい場合は?
今回クラスで定義したプロパティは、private がついているので、以下のようにはアクセスできません。
1echo $zaku_ii->$name . "の全長は " . $zaku_ii->$height . " m"; // 期待した出力は得られない
こういうときには、ゲッター(Getter)と呼ばれるメソッドをよく定義されます。
1// ゲッター
2public function get_height(){ return $this->height; }
3public function get_name(){ return $this->name; }
こうすることで、プロパティを外部から見ることのみを可能にします。(変更は不可能)
1echo $zaku_ii->get_name() . "の全長は " . $zaku_ii->get_height() . " m";
オブジェクト指向の3大要素を理解する
オブジェクト指向を理解するためには、クラスを理解することが不可欠です。
それは、クラスを理解することでオブジェクト指向の概要を把握することが可能だからです。
また、オブジェクト指向を理解することで、同時にオブジェクト指向の3大要素と呼ばれている大きなメリットを得ることができます。
オブジェクト指向の3大要素とは、「カプセル化」「継承」「ポリモーフィズム」の3つの要素を指します。
次の章では、それらについてご説明していきたいと思います。
カプセル化
オブジェクト指向を利用するメリットにカプセル化が挙げられます。
皆さんは、「カプセル」と聞くと「薬のカプセル」や「カプセルトイ」などが浮かんできたかと思います。
まさに、この「カプセル化」もこのようなイメージであり、「中身を隠す」という機能です。
中身を隠したいときに使う
オブジェクト指向は、定義したモノを「いかにシンプルに扱えるか」が重要です。
つまり、利用者は中身の仕様をよくわからなくとも簡単に扱える必要があります。
先ほどのガンダムで、どんなにattack() メソッドに複雑な処理が書かれていたとしても、外部からは$zaku_ii->attack() でOKなのです。
また、クラスで定義されたものは出来るだけ壊されないようにする必要があります。
したがって重要なプロパティはprivate を付与して外部から中身が不意に書き換えられないようにするのです。
メリット
- ソースコードを見ない限り変数名が分からないため安全性が高まる
- クラスの利用者は引数のルールとアウトプットのルールだけを理解すれば良い
- クラスの中で処理を完結させられるため保守性が上がる
継承
継承とは、すでに用意されているクラスを、他のクラスを作成する時に活用する仕組みです。
クラスを作成する時に、同じことを何度もプログラミングしなくて良いように「継承」の仕組みが用意されています。
似たようなクラスを作成する際に、「継承」を利用すれば記述が簡略化できるのです。
書き方
PHPで継承を記載するときには、以下のような書き方をします。
1class (新クラス) extends (元クラス) { (新クラスに追加したい内容) }
既に作成してあった(元クラス)を利用して、それを継承した(新クラス)を作成する方法です。
(元クラス)となる部分に基本的な要素を詰め込んでおいて、それを継承して(新クラス)を作成すると、少しの変更だけで新しいクラスを作成できます。
このときprivate がついているものは継承されず、新クラスからもアクセスできません。
もし、プロパティも継承したければ代わりに、protected にします。
少しこの点について詳しくまとめます。
注意しておきたいこと
継承を利用するときには注意しておきたいこともあります。
それは、(元クラス)の全てを継承して(新クラス)が作成されるとは限らないことです。
これは、アクセス修飾子がどのように定義されているかに左右されます。
アクセス修飾子には、3種類あります。
アクセス修飾子の種類
- public:継承可能。アクセス修飾子の定義が無い場合にはpublic扱い。
- private:継承不可能
- protected:継承可能。privateとpublicの中間的存在であり、継承したクラスからは参照できる非公開な要素
上記の通り、アクセス修飾子がprivateであると継承が適用されません。
そのため、privateが含まれているクラスを継承する場合、全く同じクラスを基礎として新しいクラスが作成されるわけではありません。
継承できるものだけを利用したクラスが作成されます。
初心者のうちは、privateにしていることを忘れてしまい、継承ができないと戸惑ってしまうこともあると思いますので特に注意しておきましょう。
ポリモーフィズム
PHPでオブジェクト指向を利用するにあたり、理解に苦しんでしまう人も多いのがポリモーフィズムです。
ポリモーフィズムは、そのまま訳すと「多様性」などの意味を持っています。
ですが、いきなり「多様性」と言われても意味が分からないことでしょう。
ザクで例えてみる
ポリモーフィズムを、ザクで例えてみましょう。
ザクは、ガンダムに「攻撃」という行動をしているイメージがありますね。
しかし、攻撃の動作は、人それぞれでイメージが異なります。
例えば、「ザク・マシンガンで攻撃する人」もいれば、「ヒート・ホークを使う人」も、そして「クラッカーを使う人」もいることでしょう。
同じ行動をするものの、その後の動作が異なるという仕組みのことをポリモーフィズムと呼ぶと思ってください。
オブジェクト指向の世界では、同じメソッドを複数のクラスで利用してその動作が異なることをポリモーフィズムと呼びます。
Aさん、Bさん、Cさんというクラスが存在しそれぞれが「攻撃する」というメソッドを持っていたときに、それぞれ異なった動作をすると捉えてみましょう。
ポリモーフィズムを実装する
それでは、継承とポリモーフィズムについてコードを見てみましょう。
1interface PGundom{ // インターフェイス
2 public function attack();
3}
4
5class Gundom implements PGundom
6{
7 // メンバ変数(プロパティ、インスタンス変数) protectedは継承される
8 protected $name;
9 protected $height;
10 protected $weight;
11 protected $color;
12
13 // コンストラクタ
14 function __construct($name, $height, $weight, $color)
15 {
16 // メンバ変数を初期化
17 $this->name = $name;
18 $this->height = $height;
19 $this->weight = $weight;
20 $this->color = $color;
21 }
22
23 // メンバ関数(メソッド)
24 public function walk(){} // 歩く
25 public function squat(){} // しゃがむ
26
27 public function attack(){ // 攻撃する
28 echo "Normal attack.\n";
29 }
30
31
32 // ゲッター
33 public function get_height(){ return $this->height; }
34
35}
36
37class Zaku extends Gundom implements PGundom { // 継承クラス
38 public function attack(){ // 攻撃する
39 echo "Zaku attack.\n";
40 }
41}
42
43$gundom = new Gundom("Gundom", 18.0, 43.4, "Normal");
44$zaku = new Zaku("ZakuII", 17.5, 56.2, "Green");
45
46$gundom->attack(); // -> Normal attack.
47$zaku->attack(); // -> Zaku attack.
新しく、ガンダムクラスを継承したザククラスclass Zaku extends Gundom を作成しました。
さらに、attack() メソッドをいずれのクラスにも実装し、それぞれ別の定義をするためにinterface を使います。
1interface PGundom{
2 public function attack();
3}
これを各クラスにimplements を用いて、明示的にこれらのインターフェイスメソッドが実装されることを定義します。
このように、各クラスでメソッド名は同じでも挙動が異なることをポリモーフィズムと呼びます。
さて、気になった方もいると思いますが、コード内に、新しい用語が2つ出てきましたので、見てみることにします。
interface
まずは「interface」です。
こちらは、クラスと同じようにインスタンスを作るために定義するものです。
しかし、クラスとは異なる点がいくつかあります。
- interfaceからインスタンスの作成はできない
- アクセス修飾子はpublicしか利用できない
- 抽象メソッドしか定義できない
大きな特徴は、直接インスタンスの生成ができないことです。
これはクラスと大きく異なります。
インスタンスを作成できないため、interfaceで定義されているものからclassで定義されているものに継承しなければなりません。
こうすることで、このインターフェイスを継承したクラスは最低でも、ここで定義したメソッドを持つことを明示的に表すことができます。
implements
この継承に利用されているのが、2つめの用語である「implements」です。
ただ、上記でご紹介した継承と用語が異なります。
実は、interfaceからclassへ継承する場合には、継承ではなく実装と呼ばれる違う動作をします。
中身は同じような動きなのですが、継承と実装で言葉が使い分けられてます。
ポリモーフィズムを利用することのメリット
- 複雑な処理であっても、ポリモーフィズムを利用することで端的に記述できる
- プログラムの保守性という面でメリットが大きい
- クラスを細分化できる
ポリモーフィズムを利用することでPHPの記述内容がシンプルになりやすくなります。
また、クラスの細分化については、クラスの絶対数は多くなる傾向になります。
しかし、細分化されていることで他に与える影響が少なくなったり、保守性が上がりますので、メリットのほうが大きいといえるのです。
オブジェクト指向でPHPをさらに使いこなす
極端な話をすると、オブジェクト指向が使えなくともPHPでプログラムを書くことは可能です。
繰り返し利用するような内容であっても、同じことを都度書けば良いのです…。
しかし、オブジェクト指向を利用することで、複雑な記述をしなくても高機能なプログラムを作成することができるようになります。
初心者PHPプログラマから初心者オブジェクト指向プログラマに進化することで、あなたのプログラミング技術は飛躍的に高まるはずです。
まずは、継承やポリモーフィズムは置いておいて、簡単なクラスから作成してみてください!
余談:PhpStormのススメ
PHPコードを書いたり、大きなアプリを作るときはIDE(統合開発環境)の使用をおすすめします!
オブジェクト指向でオススメ本
(株)ライトコードは、WEB・アプリ・ゲーム開発に強い、「好きを仕事にするエンジニア集団」です。
PHPでのご依頼・お見積もりはこちらまでお願いします。
また、PHPが得意なエンジニアを積極採用中です!詳しくはこちらをご覧ください。
※現在、多数のお問合せを頂いており、返信に、多少お時間を頂く場合がございます。
こちらの記事もオススメ!
2020.08.14PHP 特集知識編PHPおすすめフレームワーク5選PHPでコードを書くならオススメの統合開発環境「PhpStorm」クラスから理解...
2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ
「好きを仕事にするエンジニア集団」の(株)ライトコードです! ライトコードは、福岡、東京、大阪、名古屋の4拠点で事業展開するIT企業です。 現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。 いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。 システム開発依頼・お見積もり大歓迎! また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」を積極採用中です! インターンや新卒採用も行っております。 以下よりご応募をお待ちしております! https://rightcode.co.jp/recruit