PHP 的 Fluent Interface

程式技術
sharkHead

某天調整網站路由的時候看到這一句。

Route::view('/create', 'posts/create')->middleware('auth')->name('posts.create');

如果你喜歡,後面的 middleware 方法與 name 方法可以對調位置。

Route::view('/create', 'posts/create')->name('posts.create')->middleware('auth');

不知道各位有沒有想過為什麼可以這樣寫呢?每一個方法可以接續上一個方法往下寫,位置還可以任意對調。

查看 Source Code,可以發現下面這一段:

class Route
{
    ...

    public function name($name)
    {
        $this->action['as'] = isset($this->action['as']) ? $this->action['as'].$name : $name;

        return $this;
    }    

    ...

    public function middleware($middleware = null)
    {
        if (is_null($middleware)) {
            return (array) ($this->action['middleware'] ?? []);
        }

        if (is_string($middleware)) {
            $middleware = func_get_args();
        }

        $this->action['middleware'] = array_merge(
            (array) ($this->action['middleware'] ?? []), $middleware
        );

        return $this;
    }
    
    ...
}

由上述這端段 Source Code 可以發現不論是 middleware 方法與 name 方法,其中的最後一句都是。

return $this

從程式碼字面上來看,是返回目前的整個實例。但為什麼要這麼做呢?

這樣的設計被稱為 Fluent interface,目的在於讓程式碼更有可讀性。

如果不使用 Fluent interface ,但又需要呼叫一個實例中的多個方法,你可能會這樣寫。

$instance->firstMethod();
$instance->secondMethod();
$instance->thridMethod();

使用 Fluent Interface 的設計,你可以將上面的程式碼改寫成。

$instance->firstMethod()->secondMethod()->thridMethod();

程式碼會精簡非常多。

簡單寫個造車的類別,使用 Fluent interface。

<?php

class Car
{
    public string $name;
    public string $type;
    public int $weight;

    // 設定車的品牌名稱
    public function setName(string $name)
    {
        $this->name = $name;
        return $this;
    }

    // 設定車為哪一種類型
    public function setType(string $type)
    {
        $this->type = $type;
        return $this;
    }

    // 設定車體重量
    public function setWeight(int $weight)
    {
        $this->weight = $weight;
        return $this;
    }

    public function make()
    {
        echo 'Name:' . (isset($this->name) ? $this->name : 'N/A') . PHP_EOL;
        echo 'Type:' . (isset($this->type) ? $this->type : 'N/A') . PHP_EOL;
        echo 'Weight:' . (isset($this->weight) ? $this->weight : 'N/A') . PHP_EOL;
    }
}

類別寫好之後,創建一個實例並開始造車。

// 創建一個 Car 實例
$car = new Car;

$car->setName('Ferrari')->setType('racing')->setWeight(1500)->make();

執行的結果為:

Name:Ferrari
Type:racing
Weight:1500

你可以只呼叫其中幾個方法。

$car->setName('Ferrari')->setWeight(1500)->make();

// Name:Ferrari
// Type:N/A
// Weight:1500

也可以改變呼叫方法的順序。

$car->setType('racing')->setWeight(1500)->setName('Ferrari')->make();

// Name:Ferrari
// Type:racing
// Weight:1500

參考資料

sharkHead
written by
sharkHead

後端打工仔,在下班後喜歡研究各種不同的技術。稍微擅長 PHP,並偶爾涉獵前端開發。個性就像動態語言般隨興,但渴望做事能像囉嗦的靜態語言那樣嚴謹。

0 則留言