はじめに
Laravelで認証周りをいじっていた際、先輩から「ここらへん参考になるかも」と教えてもらったドキュメントを読んでいた
https://readouble.com/laravel/8.x/ja/authentication.html#the-user-provider-contract
↓Illuminate\Contracts\Auth\UserProvider
<?php namespace Illuminate\Contracts\Auth; interface UserProvider { public function retrieveById($identifier); public function retrieveByToken($identifier, $token); public function updateRememberToken(Authenticatable $user, $token); public function retrieveByCredentials(array $credentials); public function validateCredentials(Authenticatable $user, array $credentials); }
ここのupdateRemember
を呼び出したいというのが今回のやりたいことだったが、ふと「インターフェースってどうやって使うんだっけ?」となり、実装に迷った
(ここで、Controllerにある見慣れないメソッドの処理内容を知りたい時、VSCodeの拡張機能PHP Intelephense
を使ってジャンプした先がInterface
のメソッドで、そっと見て見ぬふりをしていたツケが回ってきた…)
このままではいかん、と思い、実務でインターフェースを使いこなせるようになるよう基本の勉強
インターフェースとは
https://www.php.net/manual/ja/language.oop5.interfaces.php
PHPのドキュメントの定義によると
「メソッドの実装を定義せずに、 クラスが実装する必要があるメソッドを指定するコードを作成」
できるモノ、とある。
特徴としては
- クラスと同様に定義できるが、 キーワード
class
のかわりにinterface
を用いる - メソッドの実装は全く定義しない
というものがある
メリット
使う側で呼出し元を意識しなくていい
こちらの参考記事をに沿って行くと
1, interface(メソッド名) ------ 2, 処理定義側class(メソッド名) ------- 3, 使用側class(メソッド名) Notification(`メール送信`) ─ MailNotifier(`メール送信`) ─ Hoge(`メール送信`) └ SlackNotifier(`メール送信`) ┘
こんな感じになっていて、3, class使用側では呼び出し元が2, のMailNotifierだろうがSlackNotifierだろうが関係なく使える、というのがいいポイントかな、と感じました。
追加機能も最低限の差分で追加できる
先ほどのメールを送信する
を実行した際に、「MailとSlackのほかにもう1か所メッセージを送信したい!」となった場合は先ほどの2, にclassを追加し、以下のようにすればいい。
1, interface(メソッド名) 2, 処理定義側class(メソッド名) 3, 使用側class(メソッド名) Notification(`メール送信`) ─ MailNotifier(`メール送信`) ─ Hoge(`メール送信`) └ SlackNotifier(`メール送信`) ┘ └ OtherNotifier(`メール送信`) ┘ // 追加
1, から全部処理内容書いて…みたいなことをしなくていいので、すごくコードがスッキリするイメージが持てる
使用側での可読性が向上
こちら)のソースコードの比較が分かりやすいですがインターフェースを使って書くと使用側でのコード量が圧倒的に減る
結局はバグ修正の際に開発者が最初に見るのは使用側、往々にしてビジネスロジック側なので所見でのコードがすっきりしていると、業務効率も上がってくる、ということにつながる
疑問
ここまで知識を入れた時点で浮かんだ疑問と、その回答を以下に記載
※完全に自分用メモなので、間違っている点、観点がズレている点もあるかもですが、都度修正していきます。
Q.1 interfaceって必要か?
そもそも論ですが、先ほどのこの図を見て思ったのが「1, interfaceって必要か?」という点
1, interface(メソッド名) 2, 処理定義側class(メソッド名) 3, 使用側class(メソッド名) Notification(`メール送信`) ─ MailNotifier(`メール送信`) ─ Hoge(`メール送信`) └ SlackNotifier(`メール送信`) ┘ └ OtherNotifier(`メール送信`) ┘
これを以下のようにしてしまっても、先ほどのメリットを享受できるのでは?と思った
1, interface(メソッド名) - 2, 処理定義側class(メソッド名) - 3, 使用側class(メソッド名) (delete) ─ MailNotifier(`メール送信`) ─ Hoge(`メール送信`) └ SlackNotifier(`メール送信`) ┘ └ OtherNotifier(`メール送信`) ┘
その中でinterfaceを使うメリットといえばメソッド名と変数の共通化
ができる点だな、と感じた
要は、「メソッド、引数、その引数の型」の箱を作るイメージ
Q2. オーバーライドと何が違う?
interfaceをimplements
したクラスは「1, から2,」及び「2, から3, 」で同じメソッドを使っているがふと、「オーバーライドと似ている気が、、、」と思ったけど具体的に何が違うのか?
これはシンプルで呼出し先が呼出し元に影響を与えているか、否かの違い
各々呼出し元のメソッドを呼び出した場合、
- オーバーライド:呼び出し元の処理内容を上書きして使う。一旦白紙に戻す(参照状態)
- インターフェースの使用:呼出し元に全く影響を与えず、呼出し元で定義された処理をそのまま実行する
その点で、インターフェースの利用により「1つのプロジェクトで同じメソッド名でも違う処理をしている…」みたいなカオス状態を作ることを防ぐことができる
トレイトと何が違う?
「他のクラスで定義した処理内容を使用できる」という点でトレイトとの違いは何だろうか、と思いました。
そもそも、トレイトは「単一継承の制約を減らすために作られたもの」であり、useすれば際限なく使える
https://www.php.net/manual/ja/language.oop5.traits.php
トレイトは機能ごとに作られることが多く、トレイトの中でたくさんのメソッドを定義し、処理内容もトレイト内で記述される。イメージは以下のような感じかと
1, トレイト 2, 使用側class Auth ─ Hoge Jsonable ┘ Formatter ┘
インターフェースに比べて1階層分減った分全体のディレクトリ構造はすっきりしたが、トレイトはuseできる個数に制限がないので、
「どこでどのトレイトを用いた結果、この処理が実行できているのか」
というカオス状態が生まれる危険性があるな、と推測
最後に
- インターフェースの基本、使用方法についてはイメージがついた。
- が、最後のギモンについてはまだ「完全に理解した」状態になっていないので、インターフェース、トレイト、オーバーライドの使用の際に整理が必要