PHP 的 Fluent Interface

sharkHead 程式技術 2個月前 • 0

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

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

 

參考資料
What does “return $this” mean?
Fluent interface and method chaining in PHP and JavaScript
Fluent interface - wiki


sharkHead

PHP 與 Python 菜雞工程師
最近在努力學習 TypeScript,希望可以突破慧根的限制