こんにちは、あっきー(IwswAkht)です。
今回はPHP、Laravelをベースとしたアクセス修飾子の違いについて書いていきます。
本記事を読むことで以下の項目を学ぶことができます。
- オブジェクト指向におけるアクセス修飾子の適切な設定がわかる(多言語応用可能)
- より可読性の高いコードを書くことができる
- より安全性の高いプログラムとすることができる
アクセス修飾子って何?という方におすすめでどのように使って、どのようなメリットがあるのかを解説していこうかと思います。
より良いプログラムを書くための手助けになれば幸いです。
さいしょに
アクセス修飾子にはprivate、protected、publicの3つが使用できます。
それぞれ以下の特徴があります。
- public→どこからでもアクセス可能。アクセス修飾子の省略が可能
- protected→クラス自身と継承クラスからのアクセスが可能
- private→クラス自身からアクセス可能。継承クラスからでもアクセス不可
クラスや継承という言葉でイメージしづらい場合は以下のようなイメージで考えてみてください。
- public→誰でもいける場所
- protected→家族と親戚しか行けない場所
- private→家族しか行けない場所
次に設定する優先順位は以下になります。
private > protected > public
はじめにアクセス修飾子は出来る限りprivateにできないか検討した上で、protected→publicと順番に設定するイメージです。
PHP、Laravelなどのプロジェクトにアサインする中でアクセス修飾子にとりあえずpublicを使用してるとか結構あるあるです。
アクセス修飾子を全てpublicにしてもプログラム上は確かに問題ないですが、後から大変になるケースがほとんどです。
そこでなぜアクセス修飾子になるべくpublicを設定しない方が良いのかをみていきます。
アクセス修飾子をpublicにしないメリット
結論から言いますとコードの可読性が高くなる。これに尽きます。
ミニマムなサービスのプラグラムならそれほどかもしれませんが、大規模なサービスで複数のクラスが使用される肥大化してきたプログラムの中でクラス内のメソッド全てのアクセス修飾子が全てpublicで設定されている。
そんなクラスのコードを修正してください。なんて言われたら僕だったらちょっと嫌だなって思います。
publicは言葉の通りどこからでもアクセス可能で一見便利ですが、逆を返せば「どこからでもアクセスできるの=どこでどう使われてるのかがわかりづらい」
という側面があります。
そんな中で適切にprivateやprotectedなどのアクセス修飾子が設定してあったら、このメソッドはクラス内、または継承クラスまでで完結しているんだなとアクセス修飾子をみただけでパッとわかります。
これだけでも初めてコードを読む側からしたらだいぶありがたいものです。
修正するために見るべきコード、影響範囲を確認する箇所が一気に絞れるわけですから。
この小さな積み重ねが後々のコード負債を防げると思います。
たかが修飾子と思わず、適切な修飾子を設定できるようにしっかり理解していきたいものですね。
サンプルコード
サンプルコードを見ながら挙動を確認してみます。実行環境は以下になります。
- PHP 7.4.7
- Laravel 8.44.0
まずは必要なファイルを作成していきます。以下のコマンドを実行してください。
ターミナル
1 2 3 4 5 6 7 |
$ cd app $ mkdir Classes && \ > cd Classes && \ > touch {Family.php,Relatives.php} $ cd ../../ $ php artisan make:controller SampleController |
それぞれ以下の編集をします。
app/Classes/Family.php
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 |
<?php namespace App\Classes; class Family { /** * @return string */ public function familyKey():string { echo "家の鍵を開ける。"; return $this->home(); } /** * @return string */ private function home():string { return "家族一同「おかえりなさい」\n"; } /** * @return string */ protected function visitRelatives():string { return "親戚のよっちゃんが遊びにきた \n"; } } |
app/Classes/Relatives.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?php namespace App\Classes; class Relatives extends Family { /** * @return string */ public function call():string { echo "もしもし、親戚のよっちゃんだよ。来週遊びに行くね→"; return $this->goToPlay(); } /** * @return string */ public function goToPlay():string { return parent::visitRelatives(); } } |
app/Http/Controllers/sampleController.php
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 |
<?php use App\Classes\Family; use App\Classes\Relatives; class SampleController extends Controller { public function index() { $falmily = new Family(); // 家の鍵を持ってる家族は家に帰れる echo "{$falmily->familyKey()}"; // 実行結果 => 家の鍵を開ける。家族一同「おかえりなさい」 // エラーになる。他人が勝手に人の家入ろうとしたら不法侵入です。 $others = $falmily->home(); // 実行結果 => Call to private method App\Classes\Family::home() from context 'App\Http\Controllers\SampleController' // エラーになる。親戚でも勝手に家入るのはよくないね $relatives = $falmily->home(); // 実行結果 => Call to private method App\Classes\Family::home() from context 'App\Http\Controllers\SampleController' // 電話して遊びに行く $relatives = new Relatives(); echo "{$relatives->call()}"; // 実行結果 => もしもし、親戚のよっちゃんだよ。来週遊びに行くね→親戚のよっちゃんが遊びにきた // エラーになる。知らん人がいきなり遊び来たら普通に怖い $others = $falmily->visitRelatives(); // 実行結果 => Call to protected method App\Classes\Family::visitRelatives() from context 'App\Http\Controllers\SampleController' return view('welcome'); } } |
private
先ほどのアクセス修飾子の説明をした時の例を踏襲して家族、親戚、他人というカテゴリーでサンプルを作ってみました。
LaravelではアクションのはじめはほとんどがControllerを経由しますので、別のクラスでアクセス修飾子にprivateを設定するとControllerは別クラスからのアクセスになりますので直接メソッドを実行できません。
なので、家族しか持たない家の鍵という例でsampleControllerからFamilyクラスのfamilyKey()というpublicのメソッドにアクセスします。
メソッドの中で鍵を使って家に帰るためのhome()が実行されます。
これで同じクラス内でのやりとりになりますのでアクセス修飾子がprivateでも実行が可能になります。
そのあとは直接アクセス修飾子がprivateなhome()メソッドに直接アクセスするとエラーが出る例がsampleControllerに記載されています。
protected
次にアクセス修飾子protectedに継承先からアクセスする例があります。
親戚であるRelativesクラスからcall()を実行してまずは遊びに行っていいか家族に確認をとります。
確認が取れたらgoToPlay()メソッドからparent::visitRelatives()で継承元のクラスのメソッドにアクセスします。
そのあとは継承クラスを経由しないで直接visitRelatives()にアクセスするとエラーになる例が記載されています。
ゲッターとセッター
先ほどはじめにアクセス修飾子は出来る限りprivateにできないか検討すると言いました。
また、サンプルコードの箇所でLaravelではアクションのはじめはほとんどがControllerを経由しますとも言いました。
そうすると別クラスのメソッドにアクセス修飾子privateを設定するのはあまり現実的ではないですね。
そんな時よく使われる手法がゲッターとセッターです。まとめてアクセサメソッドとも言われます。
まずは簡単なサンプルをみてみます。先ほど作ったsampleController.phpとFamily.phpに以下の編集を加えます。
app/Classes/Family.php
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 |
<?php namespace App\Classes; class Family { /** * @var string * 家族の住所 */ private $address = '東京都大田区大森1-1-1'; /** * @return string * 現在の住所 */ public function getAddress():string { return $this->address; } /** * @param string $movingAddress */ public function setAddress(string $movingAddress):void { $this->address = $movingAddress; } } |
app/Http/Controllers/sampleController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?php use App\Classes\Family; use App\Classes\Relatives; class SampleController extends Controller { public function index() { $falmily = new Family(); echo "現在の住所は{$falmily->getAddress()}です"; // 実行結果 => 現在の住所は東京都大田区大森1-1-1です $falmily->setAddress('千葉県船橋市本町1-1-1'); echo "現在の住所は{$falmily->getAddress()}です"; // 実行結果 => 引っ越し後の住所は千葉県船橋市本町1-1-1です return view('welcome'); } } |
プロパティはprivateにして、プロパティを取得するメソッドと変更するメソッド(アクセサメソッドのゲッター、セッター)を作成し、そのメソッドでプロパティの処理を記述するやり方です。
サンプルのコードの例は家族の引っ越しを例にしています。
プロパティの初期値に現在の住所を設定。privateなので直接参照はできません。
住所を参照するためにgetAssress()を経由して住所を取得します。市役所から住民票をとるみたいなイメージですかね。
住所を変更したい場合は、setAssress()を経由して住所を変更します。不動産に行ったり転出の手続きをしないと引っ越してはできませんよね。そんなイメージです。
PHPに限らずこのプロパティをprivateにして、他のクラスから参照や変更を禁止する。参照、変更するためにはアクセサメソッドを経由して行う方法は他のプログラミング言語でも共通して使えます。
これがいわゆるカプセル化というものです。
staticについて
最後にstaticについてふれておきます。
ここまでのコードはpublic function メソッド名()と書いてましたがpublic static function メソッド名()みたいな書き方をみたことある人多いのではないかと思います。
これがstaticです。メソッドだけでなくプロパティにもstaticは使用することができます。
staticの最大の特徴はインスタンス化しないでもそのままメソッドを実行できることです。
インスタンス化とはsampleController.phpでnew Family()と記述している箇所です。
staticを使うことのメリット・デメリット、その使いどころをリスト化すると以下になります。
- インスタンス化しない分メモリ消費を抑える
- 共通のメソッドを定義できる
- 共通のプロパティを定義できる
- 複数クラス間で共通の定義を共有できる
- 1つのクラスでプロパティを変更したら他に使用してる箇所すべてに影響する
- 利用用途が確定しているクラス
- メソッド単体で完結していてインスタンスの状態により影響が出ない場合
こんな感じでしょうか。ちょっとした共通処理とか定数をおいときたいとかはstaticとするのが良いです。
ただ個人的にはその挙動を理解しきってない状態で使うと不具合が出やすくなります(加えてstaticでの不具合は根が深くなりやすい)のでわからないうちは避けるのが無難かと思います。
簡単なサンプルを載せるとこんな感じです。
app/Classes/Family.php
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 |
<?php namespace App\Classes; class Family { /** * @var string * 苗字 */ public static $familyName = '田中'; /** * 結婚記念日 */ const WEDDING_ANNIVERSARY= '2021-01-01'; /** * @var int * 貯金残高 */ private static $deposits = 10000000; /** * @return int */ public static function confirmDeposits():int { return self::$deposits; } } |
app/Http/Controllers/sampleController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?php use App\Classes\Family; use App\Classes\Relatives; class SampleController extends Controller { public function index() { echo '苗字は'.Family::$familyName.'です'; // 実行結果 => 苗字は田中です echo '結婚記念日は'.Family::WEDDING_ANNIVERSARY.'です'; // 実行結果 => 結婚記念日は2021-01-01です echo '貯金残高は'.Family::confirmDeposits().'円です。'; // 実行結果 => 貯金残高は10000000円です。 echo Family::$deposits; // 実行結果 => Cannot access private property App\Classes\Family::$deposits return view('welcome'); } } |
それぞれ、苗字、結婚記念日、貯金残高という例でインスタンス化せずに参照しています。
貯金残高(deposits)はアクセス修飾子がprivateなのでconfirmDeposits()でアクセスできて、直接$depositsでアクセスするとエラーになることが確認できます。
さいごに
さいごまで読んでいただきありがとうございます。
PHPにおけるアクセス修飾子の使い方の違いについてまとめてみましたがいかがでしたでしょうか。
プログラミングをする上で基本的な部分ではありますが、最近はフレームワークからプログラミングを始めましたという方を見るとあまりこの辺を意識しないでコーディングしてる人も少なくない印象を受けました。
言語自体の理解を深めるのもプログラミングをする上では非常に大切な要素です。
この記事がどなたかに少しでも役に立ったのならば幸いです。
この記事を気に入っていただけましたらTwitterでもプログラミングに関してのツイートをリアルタイムでしていますので
ご一緒にフォローもお願いします。
僕は未経験からSESの企業にエンジニアとして転職し、その後はフリーラン、現在は受託開発企業に転職しました。
エンジニアとしていろいろな働き方を経験し、いろいろな転職サイトや転職エージェントの方にお話を伺いました。
その経験の中でだいじだと感じたことは、ITに特化した転職エージェントのサービスを利用することです。
幅広い業界に対応した転職エージェントはIT転職に特化したエージェントと比べると紹介先の数が少ないです。
そのため希望してない紹介先に転職し、思ってたのと違うとなることもあります。
そうならないためにエンジニアを目指すなら必ずIT特化の転職サービスを登録し、たくさんの選択肢の中から自分が行きたいと思う企業を探してください。
僕はフリーランスでの案件探し、受託開発企業の転職の時もレバテックのかたに紹介していただきました。
とても満足できましたので皆さんもぜひ利用してみてください!
\無料のIT系転職サービス/
/優良案件がたくさん\