iT邦幫忙

5

PHP連載12

php

這次來跟大家分享一下 PHP 5.4 新增的 Traits 語法。
大多數的程式語言只允許單一繼承,例如 Java,C++,Python,PHP ...

舉個例子

<?php
class Singleton
{
    protected static $instance = null;
    public static function getInstance()
    {
        if(NULL === static::$instance){
            static::$instance = new static();
        }
        return static::$instance;
    }
}

class Foo
{
    public function getBar()
    {
        //do something    
    }
}

class MyClass extends Foo, Singleton // <- PHP 不允許做這件事
{
    //...
}

我們寫了一個 Foo 的 Class,並且想要擴充他。但是先前我們又寫了一個 Singleton(這是一個很常用到的設計模式,可以確保資源不會被重複產生) 的 Class,因此我們會想要同時繼承 Foo 跟 Singleton。
但是很不幸的 PHP 不允許你作這件事,你只能在 Foo 跟 Singleton 這兩個Class 中選一個。

結果每次如果我們想要重用 Singleton 這個 Class 來避免重複 new 出物件,那就只能苦命的在想要使用的 Class 上重寫一次 getInstance。

這樣不是很不方便嗎怒

所以 PHP 開始向其他語言借鏡,從 Scala 學來了 Traits 這個東西。

trait 從字面上來看叫做 "特徵",也就是說他把某些具有相同特徵的片段抽離出來。

所以上面的 Code 就可以改寫成這樣

<?php
trait FreeInstance
{
    public static function releaseInstance()
    {
        static::$instance = null;
    }
}
trait Singleton
{
    protected static $instance = null;
    public static function getInstance()
    {
        if(NULL === static::$instance){
            static::$instance = new static();
        }
        return static::$instance;
    }
}

class Foo
{
    public function getBar()
    {
        //do something    
    }
}

class MyClass extends Foo
{
    use Singleton; // 使用 Singleton 這個 trait
    use FreeInstance; // 使用 FreeInstance 這個 trait
    //...
}

這樣 MyClass 具有 Foo 的功能,同時也能使用 Singleton 以及 FreeInstance。

trait 也能使用另一個 trait

例如

<?php
trait Hello {
    public function sayHello() {
        echo 'Hello ';
    }
}

trait World {
    public function sayWorld() {
        echo 'World!';
    }
}

trait HelloWorld {
    use Hello, World;
}

class MyHelloWorld {
    use HelloWorld;
}

$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();

在 trait 中存取 protected 或是 private 的限制。

<?php
<?php
class Foo
{
    private $_foo = 'private foo';
    protected $foo = 'protected foo';
}
 
class Bar extends Foo
{
    use GetFoo;
}

trait GetFoo
{
    public function getFoo()
    {
        return $this->foo;
    }
    public function getPrivateFoo()
    {
        return $this->_foo;
    }
}
$bar = new Bar();
echo $bar->getFoo();
echo $bar->getPrivateFoo(); /*這邊不能存取 private 成員,因為因為GetFoo是在 Bar 這個 class中被引用的。*/

輸出

protected fooPHP Notice: Undefined property: Bar::$_foo in /home/ricky/test/a.php on line 21

<?php
<?php
class Foo
{
    use GetFoo;
    private $_foo = 'private foo';
    protected $foo = 'protected foo';
}
 
class Bar extends Foo
{
}

trait GetFoo
{
    public function getFoo()
    {
        return $this->foo;
    }
    public function getPrivateFoo()
    {
        return $this->_foo;
    }
}
$bar = new Bar();
echo $bar->getFoo();
echo $bar->getPrivateFoo(); /*這邊就可以存取 private 成員,因為因為GetFoo是在 Foo 這個 class中被引用的。*/

輸出

protected fooprivate foo


0
fillano
iT邦超人 1 級 ‧ 2014-03-12 15:43:37

沙發...

0
0
0
imagine10255
iT邦新手 5 級 ‧ 2014-06-15 18:01:59

Hi~
之前還不知道有這個方法

想請教 trait 也同時有class的功能嗎? (感覺是 但不確定@@)

fillano iT邦超人 1 級 ‧ 2014-06-15 22:32:38 檢舉

你所謂的「class的功能」是指?

trait不能實例化,這是跟class最大的不同。在trait中,同樣可以使用"use"來把其他trait組合進來,這是有點像繼承。另外,php只能用單一繼承,但是在class或是trait中,可以任意使用多個trait。另外,父類使用的trait,在子類中還是可以使用。

trait是設計來避免複雜的繼承體系。一些功能不需要繼承也可以獨立使用的話(例如很多utils),可以適當地組織成不同的trait,這樣有需要特定功能時,直接在類別中使用特定的trait就好了。

我要留言

立即登入留言