概要
インターフェイスはクラスがメソッドを実装することを明示・保証するが、その実装はインプリメントしたクラスに託されている。
一方トレイトは、予めメソッドを具体に実装しておき、これを用いるクラス中でuse
文によって導入される。
トレイトの使い方
定義
trait
キーワードでトレイトを定義し、その中で具体のメソッドを定義する。
1 2 3 4 5 6 7 8 |
trait トレイト名 { public function メソッド名1 { } public function メソッド名2 { } ..... } |
トレイトの実装
トレイトを導入するクラスで、use
キーワードで導入するトレイトを宣言し、メソッドを呼び出す。
1 2 3 4 5 6 |
class クラス名 { use トレイト名; ..... } クラス名->メソッド名(...); |
実装例
ベースになるコード
インターフェイスで例示したRPGキャラクターのコードを使う。
- 基底クラス
CharacterClass
:名前を持ち武器攻撃のみできる - インターフェイス
Spellcaster
:呪文を唱えるメソッドspell()
を宣言 Swordfighter
クラス:CharacterClass
を継承Wizard
クラス:CharacterClass
を継承し、Spellcaster
をインプリメント- Priestクラス:
CharacterClass
を継承し、Spellcaster
をインプリメント
要件
Swordfighter
とPriest
に、クリティカルアタックの機能を持たせる- クリティカルアタックはどのクラスにおいても共通の技
トレイトの追加
以下のトレイトを追加する。
1 2 3 4 5 |
trait AttackTrait { public function criticalAttack() { echo $this->getName() . "はクリティカルアタックを決めた!!\n"; } } |
トレイトの導入
Swardfighter
クラスとPriest
クラスでトレイトを導入する。
1 2 3 4 5 6 7 8 9 10 11 |
class Swordfighter extends CharacterClass { use AttackTrait; } class Priest extends CharacterClass implements Spellcaster { use AttackTrait; public function spell() { echo $this->getName() . "は味方の傷を癒した!\n"; } } |
トレイトのメソッドの利用
クラスのインスタンスでトレイトのメソッドを呼び出す。
1 2 3 4 5 6 7 8 |
$arthur = new Swordfighter('アーサー'); $sanzo = new Priest('サンゾウ'); $arthur->criticalAttack(); $sanzo->criticalAttack(); // アーサーはクリティカルアタックを決めた!! // サンゾウはクリティカルアタックを決めた!! |
留意点
- プロパティーも宣言・定義できる
- トレイトを導入するクラスを意図した
$this
を使える - インスタンスのクラスがトレイトを導入している場合、
class_uses()
メソッドで導入しているトレイト名を得られる
class_uses~実装しているトレイトの確認
たとえば上の実装例で、別のトレイトも加えてclass_uses()
で内容を確認してみる。トレイト名をキー・値とする連想配列となっていることがわかる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
trait AnotherTrait { } class Swordfighter extends CharacterClass { use AttackTrait; use AnotherTrait; } $arthur = new Swordfighter('アーサー'); var_dump(class_uses($arthur)); // array(2) { // ["AttackTrait"]=> // string(11) "AttackTrait" // ["AnotherTrait"]=> // string(12) "AnotherTrait" // } |
これまでの例で、各クラスのインスタンスを配列に入れて、クラスに応じた動作をさせる。
トレイトを実装しているかどうかのチェックは、class_uses()で導入しているトレイトの連想配列を得て、そのキーに対象となるトレイト名があるかどうかをarray_key_exists()
でチェック。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
$party_members = []; $party_members[] = new Swordfighter('アーサー'); $party_members[] = new Wizard('ハリー'); $party_members[] = new Priest('サンゾウ'); echo "武器攻撃ターン\n"; foreach ($party_members as $member) { $member->attack(); } echo "呪文使用ターン\n"; foreach ($party_members as $member) { if ($member instanceof Spellcaster) { $member->spell(); } } echo "クリティカルアタック!\n"; foreach ($party_members as $member) { if (array_key_exists('AttackTrait', class_uses($member))) { $member->criticalAttack(); } } // 武器攻撃ターン // アーサーは武器で攻撃した! // ハリーは武器で攻撃した! // サンゾウは武器で攻撃した! // 呪文使用ターン // ハリーは攻撃魔法を唱えた! // サンゾウは味方の傷を癒した! // クリティカルアタック! // アーサーはクリティカルアタックを決めた!! // サンゾウはクリティカルアタックを決めた!! |