クマのブログ

つまづいたところ、学びを書いていきます

インターフェースとは?

はじめに

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できる個数に制限がないので、

「どこでどのトレイトを用いた結果、この処理が実行できているのか」

というカオス状態が生まれる危険性があるな、と推測

最後に

  • インターフェースの基本、使用方法についてはイメージがついた。
  • が、最後のギモンについてはまだ「完全に理解した」状態になっていないので、インターフェース、トレイト、オーバーライドの使用の際に整理が必要

参考記事

https://qiita.com/tkek321/items/a6112bc195b73438a9b0

https://qiita.com/sasakure-kei/items/212637b2f32197441a3a