首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 設計模式實戰:簡單工廠模式完整解析與計算器實作

     編輯:PHP 綜合知識

前言

簡單工廠模式是創建型模式入門,核心思想:**由一個工廠類根據參數決定實例化哪個具體產品類**。本文從模式結構、UML 關係、代碼演進、計算器實戰、優缺點分析五個維度深度解析,並對比工廠方法、抽象工廠差異,助你靈活應用。

一、模式結構與角色

┌─────────────┐       ┌──────────────┐       ┌────────────────┐
│   Client    │ ───▶  │  Factory     │ ───▶  │  Product       │
│  (調用者)   │       │  (工廠類)    │       │  (產品介面)    │
└─────────────┘       └──────────────┘       └────────┬───────┘
                                                       │
                              ┌────────────────────────┼────────────────────────┐
                              ▼                        ▼                        ▼
                       ┌─────────────┐           ┌─────────────┐           ┌─────────────┐
                       │ ConcreteA   │           │ ConcreteB   │           │ ConcreteC   │
                       │ (具體產品A) │           │ (具體產品B) │           │ (具體產品C) │
                       └─────────────┘           └─────────────┘           └─────────────┘

關鍵點

  • 工廠類通常提供 static 方法(靜態工廠方法)
  • 客戶端完全解耦具體類,僅依賴介面/抽象類
  • 屬於「類創建型模式」,不屬於 GoF 23 經典模式(是工廠方法特例)

二、代碼演進:從水果農場到計算器

階段 1:水果介面與實作

// 產品介面
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"; }
}

階段 2:簡單工廠類

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 {}

階段 3:客戶端使用

try {
    $apple = FruitGardener::factory('apple');
    $apple->plant();
    
    $grape = FruitGardener::factory('grape');
    $grape->setSeedless(true);
    $grape->grow();
} catch (BadFruitException $e) {
    echo "Error: " . $e->getMessage();
}

三、實戰:面向對象計算器

1. 抽象運算類

abstract class Operation {
    public $firstNum = 0;
    public $secondNum = 0;
    
    abstract public function getResult(): float;
}

2. 四則運算具體類

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;
    }
}

3. 工廠類

class OperationFactory {
    public function createOperation(string $operator): ?Operation {
        return match ($operator) {
            '+' => new OperationAdd(),
            '-' => new OperationSub(),
            '*' => new OperationMul(),
            '/' => new OperationDiv(),
            default => null,
        };
    }
}

4. 使用範例

$factory = new OperationFactory();
$op = $factory->createOperation('/');
$op->firstNum = 10;
$op->secondNum = 20;
var_dump($op->getResult()); // float(0.5)

四、三大工廠模式對比

維度簡單工廠工廠方法抽象工廠
GoF 分類非標準 (特例)創建型創建型
核心思想一個工廠類判斷創建定義介面,子類決定實例化創建相關/依賴對象族
擴充新產品修改工廠類 (違反 OCP)新增子類工廠 (符合 OCP)新增工廠介面實作
適用場景產品種類少、固定、邏輯簡單產品擴充頻繁、需解耦創建邏輯系列產品、跨平台、多風格
結構複雜度低中高

五、優缺點與最佳實踐

優點

  • 客戶端與具體類解耦
  • 創建邏輯集中管理,易維護
  • 靜態方法調用簡便

缺點

  • 違反開閉原則:新增產品需修改工廠類
  • 工廠類職責過重(「上帝類」風險)
  • 靜態方法難以 Mock/單元測試

最佳實踐建議

  1. 產品類少且穩定:直接用簡單工廠
  2. 需擴充/測試:升級為工廠方法模式
  3. 依賴注入容器:現代框架用 DI 容器替代手寫工廠
  4. 配置驅動:工廠讀取配置映射 ['+' => 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 容器語境下理解其歷史定位,才能做出合適的架構選擇。記住:**模式服務於業務,不為模式而模式**。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved