Menu
Coddy logo textTech

トレイト内の抽象メソッド

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

トレイトはすぐに使えるメソッドを提供できますが、トレイトを使用するクラスに特定の機能を実装させる必要がある場合があります。ここでトレイト内の抽象メソッドが役立ちます。これらは、使用するクラスが満たさなければならない契約を定義します。

トレイトが抽象メソッドを宣言する場合、そのトレイトを使用するすべてのクラスは、そのメソッドを実装しなければなりません。

<?php
trait Notifiable {
    abstract public function getEmail(): string;
    
    public function sendNotification(string $message): void {
        echo "Sending to " . $this->getEmail() . ": $message";
    }
}

class User {
    use Notifiable;
    
    public function __construct(private string $email) {}
    
    public function getEmail(): string {
        return $this->email;
    }
}

$user = new User("john@example.com");
$user->sendNotification("Welcome!");

出力:

Sending to john@example.com: Welcome!

このトレイトは sendNotification() メソッドを提供しますが、getEmail() がクラスによって実装されることに依存しています。これにより、トレイトが共通のロジックを処理し、クラス固有の詳細を実装クラスに委譲するという強力なパターンが作成されます。

クラスが抽象メソッドを実装せずにトレイトを使用すると、PHPは致命的なエラーをスローします。これにより、トレイトが特定のメソッドの存在に安全に依存できるようになり、コードの予測可能性と保守性が向上します。

challenge icon

チャレンジ

簡単

異なる製品タイプが最終価格を異なる方法で計算しつつ、抽象メソッドを持つトレイトを通じて共通の割引ロジックを共有する価格設定システムを構築しましょう。

コードを3つのファイルに分けて構成します:

  • Discountable.php — 割引機能を提供する Discountable という名前のトレイトを作成します。このトレイトには以下を含める必要があります:
    • このトレイトを使用するクラスが実装しなければならない抽象メソッド getBasePrice(): float
    • 基本価格に割引率を適用した後の価格を計算して返すメソッド applyDiscount(int $percent)
    • "Final price: $[discounted_price]" という形式で、小数点以下2桁までフォーマットされた文字列を返すメソッド getFinalPrice(int $percent)
    このトレイトはすべての割引計算を処理しますが、基本価格がいくらであるかについては各クラスからの情報に依存します。
  • Product.phpDiscountable トレイトを使用する Product クラスを作成します。トレイトファイルをインクルードしてください。コンストラクタのプロパティ昇格(constructor promotion)を使用して、private な $name (string) と private な $price (float) を定義します。製品の価格を返すために必要な getBasePrice() メソッドを実装してください。
  • main.php — Product ファイルをインクルードします。3つの入力(製品名、価格、割引率)を受け取ります。Product インスタンスを作成し、次の2行を出力してください:
    • "Base: $[price]" という形式で、小数点以下2桁までフォーマットされた基本価格
    • 割引率を指定して getFinalPrice() を呼び出した結果

このパターンは非常に強力です。なぜなら、Discountable トレイトは、getBasePrice() を実装してさえいれば、製品、サービス、サブスクリプションなど、あらゆるクラスで使用できるからです。トレイトは共有の割引ロジックを提供し、各クラスは基本価格がどこから来るのかを定義します。

チートシート

トレイトは、そのトレイトを使用するすべてのクラスで実装しなければならない抽象メソッドを宣言できます:

<?php
trait Notifiable {
    abstract public function getEmail(): string;
    
    public function sendNotification(string $message): void {
        echo "Sending to " . $this->getEmail() . ": $message";
    }
}

class User {
    use Notifiable;
    
    public function __construct(private string $email) {}
    
    public function getEmail(): string {
        return $this->email;
    }
}

トレイトはすぐに使えるメソッドを提供しつつ、クラスに特定の機能の実装を要求します。クラスが抽象メソッドを実装せずにトレイトを使用した場合、PHPは致命的エラー(fatal error)をスローします。

このパターンにより、トレイトは共通のロジックを処理し、クラス固有の詳細は実装クラスに委ねることができます。

自分で試してみよう

<?php

require_once 'Product.php';

// 入力を読み込む
$name = trim(fgets(STDIN));
$price = floatval(trim(fgets(STDIN)));
$discountPercent = intval(trim(fgets(STDIN)));

// TODO: 名前と価格を使用してProductインスタンスを作成する

// TODO: 基本価格を "Base: $[price]" の形式で小数点以下2桁までフォーマットして出力する

// TODO: 割引率を指定して getFinalPrice() を呼び出した結果を出力する

?>
quiz icon腕試し

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

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