- 了解 PHP 如何使用 Trait 來解決跨類別問題
Trait 概念與實作
- 特徵(Trait)
- Trait 與抽象類別相似,無法使用 new 來產生物件(被實例化)!
- 因為 PHP 只能單一繼承,所以無法跨多個類別使用這些類別內的方法!
- 為了減少程式碼的重複撰寫,所以 PHP 使用 Trait 來克服這個問題!
- 使用 Trait 可以讓 PHP 實現單一類別(Singleton)的使用!
- 單一 Trait 使用範例 :
- oneTrait.php
<?php class Base { public function sayHello() { echo 'Hello '; } } trait SayWorld { public function sayHello() { parent::sayHello(); echo 'World!'; } } class MyHelloWorld extends Base { use SayWorld; } $o = new MyHelloWorld(); $o->sayHello(); ?>
- oneTrait.php
- 多個 Trait 使用範例 :
- multiTrait.php
<?php trait Hello { public function sayHello() { echo 'Hello '; } } trait World { public function sayWorld() { echo 'World'; } } class MyHelloWorld { use Hello, World; public function sayExclamationMark() { echo '!'; } } $o = new MyHelloWorld(); $o->sayHello(); $o->sayWorld(); $o->sayExclamationMark(); ?>
- integrateTrait.php
<?php trait Hello { public function sayHello() { echo 'Hello '; } } trait World { public function sayWorld() { echo 'World'; } } trait HelloWorld{ use Hello, World; } class MyWorld{ use HelloWorld; } $world = new MyWorld(); echo $world->sayHello() . " " . $world->sayWorld(); //Hello World ?>
- multiTrait.php
- 優先序問題
- Trait 內可以使用 overwrite 功能來覆蓋父類別有的功能!
- 優先序的範例 : orderTrait.php
<?php trait Hello{ function sayHello() { return "Hello"; } function sayWorld() { return "Trait World"; } function sayHelloWorld() { echo $this->sayHello() . " " . $this->sayWorld(); } function sayBaseWorld() { echo $this->sayHello() . " " . parent::sayWorld(); } } class Base{ function sayWorld(){ return "Base World"; } } class HelloWorld extends Base{ use Hello; function sayWorld() { return "World"; } } $h = new HelloWorld(); $h->sayHelloWorld(); // Hello World ?>
- Trait 的衝突與別名 : 使用 insteadof 與 as
- 利用 insteadof 來解決不同 Trait 但相同方法名稱的問題
- 例 : 有一衝突的範例 confuse.php
<?php trait Game{ function play() { echo "Playing a game"; } } trait Music{ function play() { echo "Playing music"; } } class Player{ use Game, Music; } $player = new Player(); $player->play(); ?>
PS: 以上範例有兩個 Trait ,有相同的方法名稱 play()!在使用當下,就會發生衝突! - 修改後的 confuse.php 如下:
<?php trait Game{ function play() { echo "Playing a game"; } } trait Music{ function play() { echo "Playing music"; } } class Player{ use Game, Music{ //將 Game 的 play 別名成 gamePlay Game::play as gamePlay; //使用 Music 的 play 功能,取消 Game 的 play 功能! Music::play insteadof Game; } } $player = new Player(); $player->play(); $player->gamePlay(); ?>
- Trait 其它功能
- 利用 Trait 獨特的使用功能,可以取得 private 權限的類別屬性值
- 例 : getPritrait.php
<?php trait Message { function alert() { echo $this->message; } } class Messenger { use Message; private $message = "This is a message"; } $messenger = new Messenger; $messenger->alert(); //This is a message ?>
- 例 : getPritrait.php
- 利用 Trait 內可抽象化方法,強迫使用該 Trait 的類別,實作該抽象方法!
- 例 : abtractTrait.php
<?php trait Message { private $message; function alert() { $this->define(); echo $this->message; } abstract function define(); } class Messenger { use Message; function define() { $this->message = "Custom Message"; } } $messenger = new Messenger; $messenger->alert(); //Custom Message ?>
- 例 : abtractTrait.php
- Trait 的 use 與類別導入的 use 是不同的功能
- Trait 的 use : 置放於 class 內部!
- 類別導入的 use : 置放於 class 外部!
- 利用 Trait 獨特的使用功能,可以取得 private 權限的類別屬性值
本章練習:
- 修改你的 Human 類別,讓它可以使用 Bird 類別中的 fly 方法!