背景
- Laravelでの開発に従事し、実務3か月たって、タスクをちょこちょこ自力で対応できるようになってきた
- 自学の中で、過去のマージリクエストを読み込んでいるときに以下コードがふと気になった
class OrderModal
{
protected static function guestRows()
{
...
}
...
...
public static function modal(Order $order, $user)
{
...
$rows = self::guestRows($user, $order);
...
}
}
- これを見た僕は
「ん?staticってなんだっけ?staticがついていてpropaty, publicが違うのって何が違うんだっけ?」
となった。
-
まずは自分で調べて
- staticはクラスに対して依存するメソッドを作るためのメソッド
- publicとprotectedについてはアクセス権の違いの話
- publicは他のどこのクラスからも呼び出すことができるメソッドやプロパティを設定する際に使う
- protectedは本クラス(上記コードでいえば
OrderModal
)と本クラスを継承している先のクラスでのみ呼び出すことのできるメソッドやプロパティを設定する際に使う - ついでにprivateは本クラス内でのみ使うことのできるメソッドやプロパティを設定する際に使う
までの一般知識はわかった。
※static
https://www.php.net/manual/ja/language.oop5.static.php
※public, protected, (private)
https://www.php.net/manual/ja/language.oop5.visibility.php
- ただ、同時に
「実際に使う中でどう使い分けるんだろ?」
とイメージがわかなかったので、先ほど記述したコードを実際に書いた開発者に聞いたら
「え、今更?w」
と笑われた。
(まあ、正直想定内だった。。。でも、丁寧に教えてくれた彼に感謝している。)
結論
staticについて
staticを使う理由
- まずはstaticについて、これを設定している理由は
💡メソッドを呼び出す際にわざわざインスタンス化したくないから
とのことだった。
具体例
-
どういうことかというと、呼出し元を見ると理解した
例として、上記のmodalメソッドの呼び出し元を見てみる
↓modalメソッドの呼び出し元
class OrderController extends Controller
{
...
OrderModal::modal(...);
...
}
-
modalメソッドを
::
を使って呼び出している- これはクラスメソッドを呼び出す際に使う方法
-
一方で、メソッドやプロパティを呼出す方法で
->
がある- これはインスタンスメソッドを呼び出す際に使う方法
-
もしmodalメソッドがstatic無しで以下のように記述されていた場合
public function modal(Order $order, $user)
{
...
}
- 呼出し元は以下のように書くことになる
// インスタンス化
$orderModal = new OrderModal();
// インスタンスメソッドmodalを呼び出し
$orderModal->modal(...);
- つまり、インスタンス化する分、1文書く量が増える点がネックになる。
- これをしたくないために、クラスメソッドに指定したということ。
- また、この
OrderModal
自体、インスタンス化していろんなところで使われることを想定しておらず、
インスタンス化して使用しない想定→クラスメソッドで問題ない
と思うに至った、という。なるほど。
ちなみに、、、
- 実務未経験からエンジニアになって必ず抱く(自分は経験ある)、
「::selfと$this→の違いって何さ?」
という疑問もこれと同じ。
- それぞれメソッドやプロパティを呼出す際に
-
::
がクラスを呼ぶとき -
→
がインスタンスを呼ぶとき
-
に使われることが分かれば、抽象化して解釈すれば、解決できる!
※参考:https://techacademy.jp/magazine/29453
public/protected(/private)について
public/protected(/private)を使う理由
- こちらについては自分で調べた時点で理解できた
- public/protected(/private)を使い分けている理由は
💡アクセスのスコープ(≒影響範囲)を極力狭めたいため
であった。
(そのための修飾子なので当たり前の答えだが、、、)
具体例
- こちらも先ほどのコードを見て解説
↓呼出し元クラス
class OrderController extends Controller
{
...
...
OrderModal::modal(...);
...
...
}
↓呼出し先クラス
class OrderModal
{
protected static function guestRows()
{
...
}
...
...
public static function modal(Order $order, $user)
{
...
$rows = self::guestRows($user, $order);
...
}
}
大前提
- 各修飾子を話す前にメソッドやプロパティを設定する上での大前提を知っておくべきで、それは
💡なるべくプロパティやメソッドのスコープ(≒影響範囲)を狭めておく
ということ
-
これはオブジェクト指向で開発する上で、いろんなところで使われる大原則
-
これがないと、思わぬエラーに困ることがある
👇 例- 2つのクラスA, Bがある
- 各クラスで同じメソッド名
hoge
を設定している - Aクラスの
hoge
メソッドを呼び出したいのでA->hoge()
と書いた - しかし、想定外にBクラスの
hoge
クラスが呼び出されてしまう
といったことが起こる可能性がある、など
-
-
なので、なるべくprivate→protected→publicで使う優先順位を決めたほうが、想定外のエラーが発生するのを防ぐことができます。
この大前提に立ったうえでpublic/protectedを見ます。
※尚、privateは今回使っていなかったので、割愛します。。。
public
- これは先ほど出てきたmodalメソッドのように他クラスから呼び出されている
- modalメソッドがある
OrderModal
クラスと呼び出し元であるクラス(Controllerなのだが)に継承などの関係性はなし
- modalメソッドがある
- このように**「関係のない(※)」クラスからもアクセスできる**メソッドやプロパティを設定するためにpublic使う
※あくまでクラス間の「継承関係のない」という意味。
例えば、Laravelを使っているなら最低限、OrderModal
クラスはuseしてくださいね。
protected
- 一方で、protectedはpublicと違い、
OrderModal
クラスからしか呼び出されていない- 今回ではprotectedで設定した
guestRows
メソッドはmodalメソッド内でしか呼び出されていない - 自クラスのメソッドからしか呼び出されていないという事実からpublicを使う必要がないため、よりスコープを狭めたprotectedを使って
guestRows
メソッドを設定しています。
- 今回ではprotectedで設定した
※なお、ぶっちゃけここでprivateを使っても機能的には問題なしです。
ただ、このコードを書いた開発者に聞くと、
「privateまで絞ると後々使うことがある可能性をつぶしてしまうので、スコープにバファを持たせる意味でprotected使った」
とのこと。
publicを使わない場合は、privateかprotectedかは開発者やプロジェクトによってなのかな?と思った次第です。
所感
- staticについては、これまで
::
と→
は何げなく使っており、
「これはクラスメソッドだから
::
使う!!」
「これはインスタンスメソッドだから
→
使う!!」
と意識してなくて、オブジェクト指向が完全に鈍っていました…
- 特にLaravelみたいなフレームワークをつかっていると自作のクラスを作ることがあまりないため、特に要注意だな、と実感しました。
- また、アクセス修飾子については思考停止でpublicを使用していることが多く、これはなかなか自戒になりました。。。
- エラそーに書きましたが、スコープは全く意識外だったので気を付けます。