PHP 5.4.0

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

Traits

  • interface 可以形容一個 class可以做什麼

  • class 可以提供一個模組化的實作

  • 像是複製貼上的概念

  • 不會改變原先繼承的架構

  • 使用 traituse 關鍵字 (class 內使用)

  • 一個檔案一個 trait ,讓程式碼簡單容易除錯

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

參考資料