Menu
Coddy logo textTech

インターフェースによるポリモーフィズム

CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 34/91。

前のレッスンでは、子クラスが親クラスのメソッドをオーバーライドする、継承によるポリモーフィズムを見ました。インターフェースは、特に共通の動作を共有する、互いに関連のないクラスを扱う場合に、ポリモーフィズムを実現するもう1つの強力な方法を提供します。

interface では、ポリモーフィズムは契約そのものから生じます。interface を実装するすべての class は、継承階層に関係なく、特定のメソッドを持つことを保証します。

<?php
interface Notifiable {
    public function send(string $message);
}

class EmailNotifier implements Notifiable {
    public function send(string $message) {
        return "Email: " . $message;
    }
}

class SMSNotifier implements Notifiable {
    public function send(string $message) {
        return "SMS: " . $message;
    }
}

class PushNotifier implements Notifiable {
    public function send(string $message) {
        return "Push: " . $message;
    }
}

function notify(Notifiable $notifier, string $message) {
    return $notifier->send($message);
}

echo notify(new EmailNotifier(), "Hello") . "\n";
echo notify(new SMSNotifier(), "Hello") . "\n";
echo notify(new PushNotifier(), "Hello");

出力:

Email: Hello
SMS: Hello
Push: Hello

notify()関数は、任意のNotifiableオブジェクトで動作します。これらのクラスは親クラスを共有していません。同じインターフェースを実装していることを除けば、完全に無関係です。これが継承ベースのポリモーフィズムに対する主な利点です。つまり、クラスを系譜ではなく、その能力によってグループ化できます。

重要なポイント:インターフェースベースのポリモーフィズムにより、共通の動作に基づいて互いに無関係なクラスを相互に置き換えて使用できるため、コードの柔軟性が高まり、特定の実装への依存を減らせます。

challenge icon

チャレンジ

簡単

インターフェースベースのポリモーフィズムを示すストレージシステムを構築しましょう。ファイルシステムとデータベースという、互いに共通点はないものの、同じストレージインターフェースを実装する異なるストレージバックエンドを作成します。単一の関数で任意のストレージ型を扱えるようにし、interface によって完全に無関係なクラス間でもポリモーフィズムを実現できることを示します。

コードを4つのファイルに分けて整理します。

  • Storable.php: 2つのメソッドシグネチャ、save($key, $data)retrieve($key) を持つ Storable interface をDefineします。この契約により、内部でどのように実装されているかにかかわらず、任意のストレージシステムが data を保存および取得できることが保証されます。
  • FileStorage.php: Storable を implements する FileStorage class をCreateします。interface ファイルを読み込みます。class には、constructor を通じて設定される private な $directory property を持たせます。save($key, $data) を Implement し、"Saving '[data]' to file [directory]/[key].txt" を return します。retrieve($key) を Implement し、"Reading from file [directory]/[key].txt" を return します。
  • DatabaseStorage.php: 同じく Storable を implements する DatabaseStorage class をCreateします。interface ファイルを読み込みます。class には、constructor を通じて設定される private な $tableName property を持たせます。save($key, $data) を Implement し、"Inserting '[data]' into table [tableName] with key [key]" を return します。retrieve($key) を Implement し、"Selecting from table [tableName] where key = [key]" を return します。
  • main.php: 両方のストレージファイルを読み込みます。Storable パラメーター、key、data を accepts する storeData という名前の function をCreateします。この function は、key と data を指定して save() を calling した result を return する必要があります。次に、Storable パラメーターと key を accepts し、retrieve() を calling した result を返す、fetchData という名前の別の function をCreateします。

4つの入力、つまり directory パス、table name、key、そして保存する data を受け取ります。directory を使って FileStorage を、table name を使って DatabaseStorage をCreateします。storeData() function を使用して、まず file storage で data をsaveし、次に database storage でsaveします。それぞれの result を独自の行に print します。その後、fetchData() を使用して file storage から retrieve し、その result を print します。

storeData()fetchData() が両方のストレージ型で同じように動作することに注目してください。これらが気にするのは、オブジェクトが Storable を implements していることだけです。ファイルシステムとデータベースは内部の実装が完全に異なりますが、interface の契約を通じて相互に置き換え可能です。

自分で試してみよう

<?php
require_once 'FileStorage.php';
require_once 'DatabaseStorage.php';

// 入力を読み取る
$directory = trim(fgets(STDIN));
$tableName = trim(fgets(STDIN));
$key = trim(fgets(STDIN));
$data = trim(fgets(STDIN));

// TODO: Storable、key、data を受け取る関数 storeData を作成する
// key と data を指定して save() を呼び出した結果を返すこと

// TODO: Storable と key を受け取る関数 fetchData を作成する
// key を指定して retrieve() を呼び出した結果を返すこと

// TODO: directory を指定して FileStorage のインスタンスを作成する

// TODO: テーブル名を指定して DatabaseStorage のインスタンスを作成する

// TODO: storeData() を使ってファイルストレージにデータを保存し、結果を出力する

// TODO: storeData() を使ってデータベースストレージにデータを保存し、結果を出力する

// TODO: Use fetchData() to retrieve from file storage, then print the result
?>
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: PHPオンラインコンパイラ