PHP Traits 特徵

是一個局部類別的實作,它可以被混入一個以上的類別, 達到兩個不相關的類別,卻可以有相似的行為。

Traits

<?php

trait myTrait
{
    public function sayHelloWorld()
    {
        echo 'Hello World';
    }
}

class myClass
{
    use myTrait;
}

$class = new myClass();
$class->sayHelloWorld(); // Hello World

方法比較

有兩個類別 Dog, Car ,都可以從 Map 上取得他們的位置(實作getLocation()方法內容ㄧ樣)。

方法1: 一個父類別共同繼承

<?php

class Map
{
    public function getLocation() {}
}

class Dog extends Map {}

class Car extends Map {}

這樣的方法強迫讓兩個不相關的類別,共同繼承一樣的父類別,破壞了屬於他們自己的繼承架構。

方法2: 一個 Map interface 定義實作的方法

<?php

interface Map
{
    public function getLocation();
}

class Dog implements Map
{
    public function getLocation() {}
}

class Car implements Map
{
    public function getLocation() {}
}

雖然讓他們類別各自保持原有的繼承架構,但是我們必須重複在各個類別裡寫上重複相同的程式碼。

方法3: 使用 trait

<?php

trait Map
{
    public function getLocation() {}
}

class Dog
{
    use Map;
}

class Car
{
    use Map;
}

我們可以在兩個類別中都加入MapTrait,而且沒有影響到他們原本的繼承架構。

方法衝突

use 兩個 trait,可是卻有相同的方法

<?php
trait A
{
    public function smallTalk()
    {
        echo 'a';
    }

    public function bigTalk()
    {
        echo 'A';
    }
}

trait B
{
    public function smallTalk()
    {
        echo 'b';
    }

    public function bigTalk()
    {
        echo 'B';
    }
}

class Talker
{
    use A, B {
        B::smallTalk insteadof A; // B smallTalk() 取代 A smallTalk()
        A::bigTalk insteadof B;   // A bigTalk()   取代 B bigTalk()
    }
}

$talker = new Talker();
$talker->smallTalk(); // b
$talker->bigTalk();   // A

使用別名

<?php
trait A
{
    public function talk()
    {
        echo 'A';
    }
}

trait B
{
    public function talk()
    {
        echo 'B';
    }
}

class Talker
{
    use A, B {
        A::talk insteadof B; // A talk() 取代 B talk()
        B::talk as bTalk;    // B talk() 改為 bTalk()
    }
}

$talker = new Talker();
$talker->talk();  // A
$talker->bTalk(); // B

參考資料