😸

[PHP]interfaceについてのメモ

2023/07/31に公開

大まかに

interface を使用するメリットは、

  1. 制約
  2. 拡張性
  3. 代替可能性

制約

interface を委譲しているということは、 必要となるメソッドが宣言されているということ。
そして、そのメソッドは in と out が決まっているということ。
現実社会においても、「なんかいい感じの資料がほしい」となった時に、「なんかいい感じの資料」を返してくれる人に仕事を依頼するはず。

interface なんかいい感じの仕事
{
    public function なんかいい感じの資料を返す(): なんかいい感じの資料;
}

class 社員A implements なんかいい感じの仕事
{
    public function なんかいい感じの資料を返す(): なんかいい感じの資料
    {
        return new なんかいい感じの資料();
    }
}

社員Aを使用する時点で、なんかいい感じの資料を返す()が実装されていることが決まっているので、安心して仕事を依頼することができる。

拡張性

オープンクローズドの原則は、「ソフトウェアに新しく機能を追加するとき、既存のコードを変更せず新しいコードを追加するだけで済むようにしておくべきであるという意味」で、これを実現することができる。

interfaceを使用しないパターン


function いつものください(string お客さん名): 食べ物
{
    if (お客さん名 === 田中さん) {
        return new 食べ物('ラーメン');
    }
    if (お客さん名 === 佐藤さん) {
        return new 食べ物('炒飯');
    }
    throw new どちらさまですかException();
}

新しい常連鈴木さんが現れた時に if の追加が行われる。無限に続く。
もちろん、 if の部分をマッピングした配列に代替することは可能だけれど、別の条件なんて追加された時には即破綻する。


function いつものください(string お客さん名): 食べ物
{
    if (お客さん名 === 田中さん) {
        return new 食べ物('ラーメン');
    }
    if (お客さん名 === 佐藤さん) {
        return new 食べ物('炒飯');
    }
    if (お客さん名 === 鈴木さん) {
        if (晴れ) {
	    return new 食べ物('唐揚げ');
	} else {
            return new 食べ物('野菜炒め');
	}
    }
    throw new どちらさまですかException();
}

interfaceを使用するパターン

interface 常連
{
    public function いつものください(): 食べ物;
}

class 常連_田中さん implements 常連
{
    public function いつものください(): 食べ物
    {
        return new 食べ物('ラーメン');
    }
}

class 常連Factory
{
    public static function factory(string お客さん名): 常連
    {
        return new '常連_' . お客さん名();
    }
}

$常連 = 常連Factory::factory('田中さん')
$常連->いつものください(); // 食べ物('ラーメン')

interface で実装した場合は、既存ロジックを修正せずに新規に常連を委譲したクラスファイルを追加し、いつものください()を実装すればよくなるので、オープンクローズドの原則を準拠した実装になり、既存ロジックを触らなくてよい設計になる。

代替可能性

制約の内容で記述した内容が、人の目線ではなく、プログラムの目線だと in と out が決まると代替が可能と解釈できる。

interface なんかいい感じの仕事
{

public function なんかいい感じの資料を返す(): なんかいい感じの資料;

}

class 社員A implements なんかいい感じの仕事
{
    public function なんかいい感じの資料を返す(): なんかいい感じの資料
    {
        return new なんかいい感じの資料();
    }
}

class 社員B implements なんかいい感じの仕事
{
    public function なんかいい感じの資料を返す(): なんかいい感じの資料
    {
        // 評価をあげるための処理がごちゃごちゃごちゃごちゃ
	
        return new なんかいい感じの資料('内容に関係ないけど見栄えのいいデザインー!');
    }
}
$社員 = new 社員A();
$資料 = $社員->なんかいい感じの資料();

と、

$社員 = new 社員B();
$資料 = $社員->なんかいい感じの資料();

は、結局なんかいい感じの資料が返ってくるので、使用する側(会社側)からするとどちらを使用してもいい。
社員Aと社員Bは、なんかいい感じの仕事 interface のおかげで代替可能と言える。

依存関係の流れとして、実体の実装に依存関係が向くのではなく、interface に向いているこの作りは依存関係逆転の原則と呼ばれる。

interfaceでないとダメか?

継承ではダメか?という疑問が浮かびますが、継承で大丈夫かがしっかり設計出来ていれば、継承を使用するでもいいと思います。

継承は、以下の問題があります。

  • 多重継承が出来ない
  • 密結合になる

class, abstract が大きくなればなるほど無視出来なくなると思うので、使用する上では吟味が必要です。
(interface なら少しカジュアルに使えそうな温度感です。)

Discussion