PHPのインターフェースとタイプヒンティング

アプリケーション

概要

この記事はPHP Advent Calendar 2018の記事です。(ちょっと早めに投稿しています)

インターフェースはメソッドの実装を保証する”契約”的意味合いの他、タイプヒンティングによって実装を抽象に依存させる(=実装の切り替えをしやすくする)こともできる。

インターフェースの定義・実装

基本的なインターフェースの定義と実装。

<?php
interface Action
{
    public function say();
}

class Superman implements Action
{
    public function say()
    {
        echo "Hello World";
    }
}

$obj = new Superman();
$obj->say();

インターフェースによる機能と実装の分離

タイプヒンティングでインターフェース型を指定すると実装に柔軟性を持たせることができる。

<?php
interface HeroAction
{
    public function say();
}

class Superman implements HeroAction
{
    public function say()
    {
        echo "I'm a Superman";
    }
}

class Human
{
    public function say()
    {
        echo "I'm a Human";
    }
}

class Bot
{
    public function do(HeroAction $heroAction) // 引数にインターフェース型を指定
    {
        $heroAction->say();
    }
}

$superMan = new SuperMan();
$human = new Human();
$bot = new Bot();

$bot->do($superMan); // I'm a Superman
$bot->do($human); // PHP Fatal error:  Uncaught TypeError: Argument 1 passed to Bot::do() must implement interface HeroAction, instance of Human given, called in ....

Supermanの実装を取りやめて、Hypermanの実装に切り替える。

<?php
interface HeroAction
{
    public function say();
}

// class Superman implements HeroAction
// {
//     public function say()
//     {
//         echo "I'm a Superman";
//     }
// }

class Hyperman implements HeroAction
{
    public function say()
    {
        echo "I'm a Hyperman";
    }
}

class Human
{
    public function say()
    {
        echo "I'm a Human";
    }
}

class Bot
{
    public function do(HeroAction $heroAction) // 引数にインターフェース型を指定
    {
        $heroAction->say();
    }
}

// $superMan = new SuperMan();
$hyperMan = new HyperMan();
$human = new Human();
$bot = new Bot();

// $bot->do($superMan); // I'm a Superman
$bot->do($hyperMan); // I'm a Hyperman
$bot->do($human); // PHP Fatal error:  Uncaught TypeError: Argument 1 passed to Bot::do() must implement interface HeroAction, instance of Human given, called in ....

もし、Botクラスのdoメソッドがインターフェースではなく、Supermanクラスに依存していた場合、実装を交換する手間が増えてしまう。

class Bot
{
    public function do(Superman $superman) // 引数にインターフェース型を指定
    {
        $superman->say();
    }
}

参考