トレイトと継承の比較
CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 55/91。
traits の仕組みを理解したところで、traits と Inheritance のどちらを使うべきかを明確にしましょう。どちらもコードの再利用を可能にしますが、目的が異なり、それぞれ独自の特徴があります。
Inheritanceは「is-a」関係を確立します。Animalを拡張するDog classは、犬が動物であることを意味します。子classは親の同一性を継承し、親の型が期待されるあらゆる場所で使用できます。
トレイトは「has-a」機能を提供します。クラスがLoggableトレイトを使用すると、has logging 機能を持ちますが、ロガーになるわけではありません。トレイトは、クラスが本質的に何であるかを変えずに、振る舞いを追加します。
<?php
// 継承: Dog は Animal である
class Animal {
public function breathe(): void {
echo "Breathing";
}
}
class Dog extends Animal {
public function bark(): void {
echo "Woof!";
}
}
// トレイト: Dog はロギング機能を持つ
trait Loggable {
public function log(string $msg): void {
echo "Log: $msg";
}
}
class Cat extends Animal {
use Loggable;
}
覚えておくべき主な違い:
| 側面 | Inheritance | トレイト |
|---|---|---|
| 関係 | is-a | has-a capability |
| 制限 | 単一の親のみ | 複数のトレイトを使用可能 |
| 型チェック | instanceofで動作 | 型の同一性なし |
| 最適な用途 | 階層的な関係 | 無関係なクラス間で共有する振る舞い |
クラスが共通のアイデンティティを共有している場合は、継承を使用します。無関係なクラスに同じ機能が必要であり、同じ系統に属しているわけではない場合は、トレイトを使用します。
チャレンジ
簡単継承とトレイトをいつ使うべきかを示す車両システムを構築しましょう。継承によって共通のアイデンティティを持つ車両の階層を作成し、トレイトによって横断的な機能を追加します。
コードを4つのファイルに分けて整理します。
Trackable.php: GPS追跡機能を提供するTrackableというトレイトを作成します。このトレイトには、次の要素を定義します。- privateプロパティ
$location(string) - 現在の位置を保存する
setLocation(string $location)メソッド "Location: [location]"を返すgetLocation()メソッド
- privateプロパティ
Vehicle.php: 「is-a」関係を確立する基底Vehicleクラスを作成します。トレイトファイルをインクルードします。コンストラクター・プロモーションを使用して、protectedな$brandプロパティを定義します。"Vehicle: [brand]"を返すdescribe()メソッドを追加します。フリート内のすべての車両がGPSを持つため、このクラスではTrackableトレイトを使用します。Car.php:VehicleをextendsするCarクラスを作成します。Vehicleファイルをインクルードします。コンストラクター・プロモーションを使用して、brandとprivateな$doors(integer)プロパティを受け取ります。brandを指定してparentコンストラクターを呼び出します。"Car: [brand] with [doors] doors"を返すようにdescribe()メソッドをOverrideします。main.php: Carファイルをインクルードします。brand名、ドアの数、位置を表す文字列という3つの入力を受け取ります。Carのinstanceを作成して位置を設定し、その後、次の2行を出力します。describe()を呼び出した結果getLocation()を呼び出した結果
ここでは、継承とトレイトがどのように連携するかに注目してください。Car is a Vehicle(継承によってアイデンティティが確立されます)が、Vehicle has追跡機能を持ちます(トレイトによって振る舞いが追加されます)。何かが車両かどうかを確認するためにinstanceof Vehicleを使用できますが、確認対象となる「Trackable」型はありません。これが、この2つのアプローチの重要な違いです。
自分で試してみよう
<?php
// メインファイル - Carインスタンスを作成してテストする
// TODO: Car.phpファイルをインクルードする
// 入力を読み取る
$brand = trim(fgets(STDIN));
$doors = intval(trim(fgets(STDIN)));
$location = trim(fgets(STDIN));
// TODO: brandとdoorsでCarインスタンスを作成する
// TODO: 車のlocationを設定する
// TODO: describe()の結果を出力する
// TODO: getLocation()の結果を出力する
?>このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: PHPオンラインコンパイラ