複数のトレイトの利用
CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 52/91。
単一のトレイトは便利ですが、本当の力は1つのクラスで複数のトレイトを組み合わせたときに発揮されます。PHPでは、必要な数だけトレイトを取り込むことができ、一度に複数のソースからクラスに機能を持たせることができます。
複数のトレイトを使用するには、useキーワードの後にカンマで区切って並べるだけです。
<?php
trait Timestampable {
public function getTimestamp(): string {
return date('Y-m-d H:i:s');
}
}
trait Identifiable {
private string $id;
public function generateId(): void {
$this->id = uniqid('id_');
}
public function getId(): string {
return $this->id;
}
}
class Post {
use Timestampable, Identifiable;
public function __construct(public string $title) {
$this->generateId();
}
}
$post = new Post("Hello World");
echo $post->getId() . " - " . $post->getTimestamp();
出力:
id_6789abc123 - 2024-01-15 10:30:00Postクラスは、両方のトレイトのメソッドを持つようになりました。各トレイトは独自の機能を提供します。Timestampableは時間関連のメソッドを提供し、Identifiableは一意のIDを処理します。このアプローチにより、各トレイトが単一の責任に集中し続けるため、コードのモジュール化が進み、再利用性が高まります。
2つのトレイトが同じ名前のメソッドを持っている場合、何が起こるでしょうか?それは競合する状況であり、次のレッスンで詳しく見ていきます。
チャレンジ
簡単複数のトレイトを組み合わせて、製品に価格設定と在庫管理の両方の機能を持たせる製品管理システムを構築しましょう。各トレイトは単一の責任を担い、製品クラスは両方から機能を得ることになります。
コードを3つのファイルに分けて構成します:
Priceable.php— 価格設定機能を処理するPriceableという名前のトレイトを作成します。このトレイトには以下を含める必要があります:- プライベートプロパティ
$price(float) - 価格を保存するメソッド
setPrice(float $price) - 価格を返すメソッド
getPrice() - 価格を
"$[price]"形式で小数点以下2桁でフォーマットして返すメソッドgetFormattedPrice()
- プライベートプロパティ
Stockable.php— 在庫を管理するStockableという名前のトレイトを作成します。このトレイトには以下を含める必要があります:- プライベートプロパティ
$stock(integer) - 在庫数を保存するメソッド
setStock(int $quantity) - 現在の在庫を返すメソッド
getStock() - 在庫が0より大きい場合は
true、そうでない場合はfalseを返すメソッドisInStock()
- プライベートプロパティ
main.php— 両方のトレイトファイルをインクルードし、PriceableとStockableの両方のトレイトを使用するProductクラスを作成します。コンストラクタのプロパティ昇格(constructor promotion)を使用して、パブリックな$nameプロパティを定義します。コンストラクタは価格と在庫数も受け取り、トレイトのメソッドを使用してそれらを設定する必要があります。製品名、価格、在庫数の3つの入力を受け取ります。これらの値を使用して製品を作成し、次の4行を出力してください:
- 製品名
- フォーマットされた価格(
getFormattedPrice()を使用) - 在庫数
- 在庫がある場合は
"Available"、ない場合は"Out of stock"
これは、複数のトレイトがどのように連携するかを示しています。Product クラスは、あるトレイトから価格設定機能を、別のトレイトから在庫管理機能を得ることで、各関心をきれいに分離したまま、それらを組み合わせてフル機能の製品を実現します。
自分で試してみよう
<?php
require_once 'Priceable.php';
require_once 'Stockable.php';
// TODO: Priceable と Stockable の両方のトレイトを使用する Product クラスを作成します
// public $name プロパティにコンストラクタのプロパティ昇格(constructor promotion)を使用してください
// コンストラクタは name、price、stock を受け取り、トレイトのメソッドを使用して price と stock を設定する必要があります
class Product {
// TODO: ここでトレイトを使用します
// TODO: $name のコンストラクタのプロパティ昇格を使用してコンストラクタを実装します
// そして、トレイトのメソッドを使用して price と stock を設定します
}
// 入力を読み込む
$name = trim(fgets(STDIN));
$price = floatval(trim(fgets(STDIN)));
$stock = intval(trim(fgets(STDIN)));
// TODO: 入力値を使用して Product インスタンスを作成します
// TODO: 商品名を表示します
// TODO: getFormattedPrice() を使用してフォーマットされた価格を表示します
// TODO: 在庫数を表示します
// TODO: 在庫がある場合は "Available"、ない場合は "Out of stock" を表示します
?>このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。