PHP 的 Fluent Interface
某天調整網站路由的時候看到這一句。
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