Menu
Coddy logo textTech

トレイト vs 継承

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

トレイトの仕組みを理解したところで、トレイトと継承のどちらを使うべきかを明確にしましょう。どちらもコードの再利用を可能にしますが、それぞれ目的が異なり、明確な特徴があります。

継承は「is-a」関係を構築します。Animalを拡張するDogクラスは、犬が動物であるis an)ことを意味します。子クラスは親のアイデンティティを継承し、親の型が期待される場所であればどこでも使用できます。

トレイトは「has-a」機能を提供します。クラスが Loggable トレイトを使用すると、そのクラスはロギング機能を持ちますが、ロガーそのものになるわけではありません。トレイトは、クラスが根本的に何であるかを変えることなく、振る舞いを追加します。

<?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;
}

覚えておくべき主な違い:

側面継承トレイト
関係is-ahas-a(機能の保持)
制限単一の親のみ複数のトレイトが可能
型チェックinstanceof で動作する型のアイデンティティなし
最適な用途階層関係関連のないクラス間での動作の共有

クラスが共通のアイデンティティを共有する場合は、継承を使用してください。関連のないクラスが、同じ継承ツリーの一部になることなく同じ機能を必要とする場合は、トレイトを使用してください。

challenge icon

チャレンジ

簡単

継承とトレイトをいつ使用すべきかを示す車両システムを構築しましょう。継承を通じて共通のアイデンティティを共有する車両の階層を作成し、トレイトを通じて横断的な機能を追加します。

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

  • Trackable.php — GPS追跡機能を提供する Trackable という名前のトレイトを作成します。このトレイトには以下を含める必要があります:
    • プライベートプロパティ $location (string)
    • 現在の場所を保存するメソッド setLocation(string $location)
    • "Location: " . $this->location を返すメソッド getLocation()
    このトレイトは「has-a(持っている)」機能を表します。どの車両も追跡機能を持つことができますが、追跡機能を持っているからといってそれが車両になるわけではありません。
  • Vehicle.php — 「is-a(である)」関係を確立するベースとなる Vehicle クラスを作成します。トレイトファイルをインクルードしてください。コンストラクタのプロパティ昇格(constructor promotion)を使用して、protectedな $brand プロパティを定義します。"Vehicle: " . $this->brand を返すメソッド describe() を追加します。このクラスは、フリート内のすべての車両がGPSを持つため、Trackable トレイトを使用する必要があります。
  • Car.phpVehicle を継承する Car クラスを作成します。Vehicleファイルをインクルードしてください。コンストラクタのプロパティ昇格を使用して、ブランド名とプライベートな $doors (integer) プロパティを受け取ります。ブランド名を使用して親クラスのコンストラクタを呼び出します。describe() メソッドをオーバーライドして、"Car: " . $this->brand . " with " . $this->doors . " doors" を返すようにします。
  • main.php — Carファイルをインクルードします。ブランド名、ドアの数、場所の文字列の3つの入力を受け取ります。Car インスタンスを作成し、その場所を設定してから、次の2行を出力します:
    • describe() を呼び出した結果
    • getLocation() を呼び出した結果

ここで継承とトレイトがどのように連携しているかに注目してください。CarVehicle であり(継承がアイデンティティを確立)、Vehicle は追跡機能を「持っています」(トレイトが振る舞いを追加)。instanceof Vehicle を使用して何かが車両であるかどうかを確認できますが、確認対象となる「Trackable」型は存在しません。これがこれら2つのアプローチの主な違いです。

チートシート

「is-a」関係には継承(inheritance)を、「has-a」の能力にはトレイト(traits)を使用します。

継承はアイデンティティを確立します。子クラスは親クラスの一種(is a)であり、親の型が期待される場所ならどこでも使用できます。

トレイトはクラスのアイデンティティを変えることなく振る舞いを追加します。クラスはその機能を持って(has)いますが、そのものになるわけではありません。

<?php
// 継承: DogはAnimalである (IS an Animal)
class Animal {
    public function breathe(): void {
        echo "Breathing";
    }
}

class Dog extends Animal {
    public function bark(): void {
        echo "Woof!";
    }
}

// トレイト: Catはロギング機能を持っている (HAS logging capability)
trait Loggable {
    public function log(string $msg): void {
        echo "Log: $msg";
    }
}

class Cat extends Animal {
    use Loggable;
}
項目継承トレイト
関係is-ahas-a 能力
制限単一の親のみ複数のトレイトを使用可能
型チェックinstanceof で動作する型のアイデンティティを持たない
最適な用途階層的な関係関連のないクラス間での共有される振る舞い

クラスが共通のアイデンティティを共有する階層的な関係には継承を使用します。関連のないクラス間で共有される振る舞いにはトレイトを使用します。

自分で試してみよう

<?php
// メインファイル - Carインスタンスの作成とテスト

// TODO: Car.phpファイルをインクルードする

// 入力を読み込む
$brand = trim(fgets(STDIN));
$doors = intval(trim(fgets(STDIN)));
$location = trim(fgets(STDIN));

// TODO: ブランドとドア数でCarインスタンスを作成する

// TODO: 車の場所を設定する

// TODO: describe()の結果を出力する

// TODO: getLocation()の結果を出力する
?>
quiz icon腕試し

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

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