委托模式是軟體設計模式中的基礎技巧:接收請求的物件將處理邏輯委托給另一個物件執行。許多進階模式(狀態、策略、訪問者)本質上都是委托模式在特定場景的應用。PHP 5.3+ 的反射機制讓動態委托成為可能。
<?php
class Fruit {
public function callFruit() {
echo "Generate an Apple";
}
}
class FruitDelegator {
private $targets = array();
public function __construct() {
$this->targets[] = new Fruit();
}
public function __call($name, $args) {
foreach ($this->targets as $obj) {
$reflection = new ReflectionClass($obj);
if ($method = $reflection->getMethod($name)) {
if ($method->isPublic() && !$method->isAbstract()) {
return $method->invoke($obj, $args);
}
}
}
throw new BadMethodCallException("Method $name not found in delegated objects");
}
}
// 使用範例
$obj = new FruitDelegator();
$obj->callFruit(); // 輸出: Generate an Apple
?>
<?php
class Color {
public function callColor() {
echo "Generate Red";
}
}
class Size {
public function callSize() {
echo "Size: Large";
}
}
class MultiDelegator {
private $targets = array();
public function addObject($obj) {
$this->targets[] = $obj;
}
public function __call($name, $args) {
foreach ($this->targets as $obj) {
$reflection = new ReflectionClass($obj);
if ($method = $reflection->getMethod($name)) {
if ($method->isPublic() && !$method->isAbstract()) {
return $method->invoke($obj, $args);
}
}
}
throw new BadMethodCallException("Method $name not found in any delegated object");
}
}
// 使用範例
$delegator = new MultiDelegator();
$delegator->addObject(new Color());
$delegator->addObject(new Size());
$delegator->callColor(); // 輸出: Generate Red
$delegator->callSize(); // 輸出: Size: Large
?>
| 方法/屬性 | 用途 |
|---|---|
| ReflectionClass::getMethod() | 取得方法反射物件,可檢查可見性、參數、回傳型別等 |
| ReflectionMethod::isPublic() | 確認方法為 public,避免誤呼叫 protected/private 方法 |
| ReflectionMethod::isAbstract() | 排除抽象方法,防止實例化錯誤 |
| ReflectionMethod::invoke() | 動態呼叫方法,支援參數陣列傳遞 |
ReflectionMethod 物件#[ReturnTypeWillChange] 屬性處理回傳型別协变method_exists() + call_user_func_array() 簡化輕量級場景PHP 7.4+ 引入 箭頭函數 與 First-class callable,PHP 8.1+ 支援 new class() 匿名類,可構建更優雅的委托結構:
<?php
// PHP 8.1+ 現代寫法
$delegator = new class {
private array $targets = [];
public function add(object $obj): self {
$this->targets[] = $obj;
return $this;
}
public function __call(string $name, array $args) {
foreach ($this->targets as $target) {
if (method_exists($target, $name)) {
return $target->$name(...$args);
}
}
throw new BadMethodCallException("$name not found");
}
};
$delegator->add(new Color())->add(new Size());
$delegator->callColor();
?>