簡單工廠模式是創建型模式入門,核心思想:**由一個工廠類根據參數決定實例化哪個具體產品類**。本文從模式結構、UML 關係、代碼演進、計算器實戰、優缺點分析五個維度深度解析,並對比工廠方法、抽象工廠差異,助你靈活應用。
┌─────────────┐ ┌──────────────┐ ┌────────────────┐
│ Client │ ───▶ │ Factory │ ───▶ │ Product │
│ (調用者) │ │ (工廠類) │ │ (產品介面) │
└─────────────┘ └──────────────┘ └────────┬───────┘
│
┌────────────────────────┼────────────────────────┐
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ ConcreteA │ │ ConcreteB │ │ ConcreteC │
│ (具體產品A) │ │ (具體產品B) │ │ (具體產品C) │
└─────────────┘ └─────────────┘ └─────────────┘
static 方法(靜態工廠方法)// 產品介面
interface Fruit {
public function plant();
public function grow();
public function harvest();
}
// 具體產品
class Apple implements Fruit {
private $treeAge;
public function plant() { echo "Apple planted.\n"; }
public function grow() { echo "Apple growing.\n"; }
public function harvest() { echo "Apple harvested.\n"; }
public function setTreeAge($age) { $this->treeAge = (int)$age; }
}
class Grape implements Fruit {
private $seedless;
public function plant() { echo "Grape planted.\n"; }
public function grow() { echo "Grape growing.\n"; }
public function harvest() { echo "Grape harvested.\n"; }
public function setSeedless($bool) { $this->seedless = (bool)$bool; }
}
class Strawberry implements Fruit {
public function plant() { echo "Strawberry planted.\n"; }
public function grow() { echo "Strawberry growing.\n"; }
public function harvest() { echo "Strawberry harvested.\n"; }
}
class FruitGardener {
public static function factory($type) {
$type = strtolower($type);
switch ($type) {
case 'apple': return new Apple();
case 'grape': return new Grape();
case 'strawberry': return new Strawberry();
default:
throw new BadFruitException("Unknown fruit: $type");
}
}
}
class BadFruitException extends Exception {}
try {
$apple = FruitGardener::factory('apple');
$apple->plant();
$grape = FruitGardener::factory('grape');
$grape->setSeedless(true);
$grape->grow();
} catch (BadFruitException $e) {
echo "Error: " . $e->getMessage();
}
abstract class Operation {
public $firstNum = 0;
public $secondNum = 0;
abstract public function getResult(): float;
}
class OperationAdd extends Operation {
public function getResult(): float { return $this->firstNum + $this->secondNum; }
}
class OperationSub extends Operation {
public function getResult(): float { return $this->firstNum - $this->secondNum; }
}
class OperationMul extends Operation {
public function getResult(): float { return $this->firstNum * $this->secondNum; }
}
class OperationDiv extends Operation {
public function getResult(): float {
if ($this->secondNum == 0) {
throw new Exception('除數不能為零');
}
return $this->firstNum / $this->secondNum;
}
}
class OperationFactory {
public function createOperation(string $operator): ?Operation {
return match ($operator) {
'+' => new OperationAdd(),
'-' => new OperationSub(),
'*' => new OperationMul(),
'/' => new OperationDiv(),
default => null,
};
}
}
$factory = new OperationFactory();
$op = $factory->createOperation('/');
$op->firstNum = 10;
$op->secondNum = 20;
var_dump($op->getResult()); // float(0.5)
| 維度 | 簡單工廠 | 工廠方法 | 抽象工廠 |
|---|---|---|---|
| GoF 分類 | 非標準 (特例) | 創建型 | 創建型 |
| 核心思想 | 一個工廠類判斷創建 | 定義介面,子類決定實例化 | 創建相關/依賴對象族 |
| 擴充新產品 | 修改工廠類 (違反 OCP) | 新增子類工廠 (符合 OCP) | 新增工廠介面實作 |
| 適用場景 | 產品種類少、固定、邏輯簡單 | 產品擴充頻繁、需解耦創建邏輯 | 系列產品、跨平台、多風格 |
| 結構複雜度 | 低 | 中 | 高 |
['+' => OperationAdd::class],新增產品只改配置class ConfigurableFactory {
private $map = [];
public function __construct(array $config) {
$this->map = $config;
}
public function create(string $key) {
if (!isset($this->map[$key])) {
throw new InvalidArgumentException("Unknown type: $key");
}
$class = $this->map[$key];
return new $class();
}
}
// 配置檔/依賴注入設定
$config = [
'+' => OperationAdd::class,
'-' => OperationSub::class,
'*' => OperationMul::class,
'/' => OperationDiv::class,
];
$factory = new ConfigurableFactory($config);
簡單工廠模式是理解工廠家族的基石。雖違反 OCP,但在「產品固定、邏輯簡單、團隊中小型」場景下極具性價比。掌握其向工廠方法、抽象工廠演進的路徑,並在現代 DI 容器語境下理解其歷史定位,才能做出合適的架構選擇。記住:**模式服務於業務,不為模式而模式**。