首頁 >後端開發 >php教程 >[Modern PHP] 第二章 新功能之三 Traits

[Modern PHP] 第二章 新功能之三 Traits

WBOY
WBOY原創
2016-07-30 13:31:20972瀏覽

Traits

我的許多PHP開發者朋友都不太了解traits,這是PHP 5.4.0中引入的新概念。 traits看起來像介面但是用起來像類,那究竟是什麼呢?兩者都不是。

一個trait擁有部分實現(譬如常量、屬性和方法),可以被植入到一個或多個實際的PHP類中。 trait有兩個職責:表示一個類別可以做什麼(類似介面);提供一個模組化的實作(類似類別)。

在其他的語言中你也許已經對traits有了一定的了解。譬如Ruby的modules及mixins功能就和PHP的traits很類似。

我們為什麼要使用traits

PHP語言使用的是經典的繼承模型。這意味著你從一個提供了基本實作的通用的根類別開始。從根類創造出更具體的類,它們直接繼承了父類的各種實作。這叫做繼承層次,許多程式語言使用的都是這種公用的模式。

為了方便理解,假設你穿越回高中學習生物。還記得你學習的生物的界門綱目科屬種嗎?總共有六大界,界派生出門、門派生出綱、綱派生出目、目派生出科、科派生出屬、屬後面是種。在物種的層級上的每次向下的派生都代表著具體的特性。

經典的繼承模型大多數情況下都能工作的很好。但是,如果有兩個毫無關聯的類別需要實現類似的行為該如何解決?例如,一個PHP類叫RetailStore,而另一個PHP類叫Car,它們兩個可以是說是完全風馬牛不相及的兩個類,在繼承關係上根本無法共享一個公用的父類。然而兩個類別都需要使用地理位置中的經度和緯度來顯示地圖座標。

我們創造traits就是來解決這個問題的。它們可以將部分實現注入到不相干的類別中。 traits也同樣利於程式碼的重用。

遇到這個問題,我的第一個解決方案(也是最糟糕的)是創造一個公共的父類Geocodable用來給RetailStore和Car這兩個類別來繼承。這個解決方案實在是太糟糕了,因為強行讓兩個毫不相干的類別取共享一個公共的祖先,在它們各自的繼承層級上都顯得格外彆扭。

我的第二個解決方案(稍微好點)是創造一個Geocodable介面來定義實作地理位置需要哪些方法。 RetialStore和Car兩個類別都可以實作這個Geocodable介面。讓每個類別都能保留各自自然的繼承關係確實是個很好的解決方案。但是我們還是需要在每個類別裡都重複的去實作介面裡的定義,這可不是一個DRY的方案。

DRY是Do not repeat yourself的縮寫。作為一個好的程式設計習慣,我們永遠不要在多個地方重複同樣的程式碼。不能出現因為改了一處程式碼,而被動的還要去修改其他地方同樣的程式碼的狀況。

我的第三個方案(最佳方案)是建構一個Geocodable的trait,在裡面定義並實現相關的方法。我可以在不打亂類別的繼承層級的情況下把Geocodable的trait加到RetailStore類別和Car類別中。

如何構造一個trait

下面展示的是如何定義一個PHP的trait:

<?php
trait MyTrait {
    // 此处是trait的具体实现
}

作為一個好習慣,我們應該做到一個文件和一個trait,就像類和接口的定義一樣。

讓我們回到我們的Geocodable範例來更好的示範一下trait的使用。我們都知道RetailStore類別和Car類別需要支援地理位置的定位功能,而我們也都能認同繼承和介面並不是最佳方案。取而代之,我們建構一個Geocodable trait來傳回一個可以在地圖上標記的經度和緯度座標。範例2-12中是我們完整的Geocodable trait。

例子 2-12 Geocodable trait的定義

<span style="font-size:14px;"><?php
trait Geocodable {
    /** @var string */
    protected $address;

    /** @var \Geocoder\Geocoder */
    protected $geocoder;

    /** @var \Geocoder\Result\Geocoded */
    protected $geocoderResult;

    public function setGeocoder(\Geocoder\GeocoderInterface $geocoder)
    {
        $this->geocoder = $geocoder;
    }

    public function setAddress($address)
    {
        $this->address = $address;
    }

    public function getLatitude()
    {
        if (isset($this->geocoderResult) === false) {
            $this->geocodeAddress();
        }

        return $this->geocoderResult->getLatitude();
    }

    public function getLongitude()
    {
        if (isset($this->geocoderResult) === false) {
            $this->geocodeAddress();
        }

        return $this->geocoderResult->getLongitude();
    }

    protected function geocodeAddress()
    {
        $this->geocoderResult = $this->geocoder->geocode($this->address);

        return true;
    }
}</span>

Geocodable trait僅僅只定義了執行地理位置所需的屬性和方法所需的任何屬性和方法。

我們的Geocodable trait定義了三個類別的屬性:

未完待續…

以上就介紹了[Modern PHP] 第二章 新功能之三 Traits,包括了方面的內容,希望對PHP教程有興趣的朋友有所幫助。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn