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

PHP 原型模式深度解析:Constructor 與 Initialize 分離、Zend Framework 實戰與完整代碼範例

     編輯:PHP 綜合知識

前言

當需要大量建立「大部分相同、小部分不同」的物件時,每次 new 完整初始化開銷過大。**原型模式** 讓我們先建立一個「原型物件」,後續透過 clone 複製並只初始化差異部分,大幅提升效能。本文結合 Zend Framework 2 原始碼與經典教學範例,完整解析 PHP 實作要點。

核心角色

角色職責PHP 對應
抽象原型宣告克隆介面介面或抽象類別
具體原型實作克隆邏輯實作 __clone() 的類別
客戶端請求克隆使用 clone 關鍵字

關鍵技巧:__construct() 與 initialize() 分離

痛點: 建構子若包含昂貴操作(資料庫連線、大量記憶體分配),每次 new 都會重複執行。
解法:

  • __construct():只做**共用、昂貴、不變**的初始化(如 DB Adapter、TableGateway 設定)
  • initialize($data):做**每個實例獨有**的輕量初始化(如當筆資料行)

實戰一:Zend Framework 2 TableGateway + ResultSet

Module.php 服務工廠:

public function getServiceConfig() {
    return [
        'factories' => [
            'Album\Model\AlbumTable' => function ($sm) {
                $tableGateway = $sm->get('AlbumTableGateway');
                return new AlbumTable($tableGateway);
            },
            'AlbumTableGateway' => function ($sm) {
                $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
                // 原型:空的 ResultSet + 空的 Album 實體原型
                $resultSetPrototype = new ResultSet();
                $resultSetPrototype->setArrayObjectPrototype(new Album());
                
                return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
            },
        ],
    ];
}

流程:

  1. TableGateway::select() 執行查詢
  2. 內部 clone $this->resultSetPrototype 取得新 ResultSet
  3. 逐行 $resultSet->initialize($rowData) 填入資料
  4. 回傳包含多個已初始化 Entity 的 ResultSet

實戰二:獨立完整範例(參考 Ralph Schindler 教學)

// 共用資料庫適配器(昂貴資源)
class DbAdapter {
    public function fetchAllFromTable(string $table): array {
        // 模擬查詢回傳多行陣列
        return [['id'=>1,'name'=>'Alice'], ['id'=>2,'name'=>'Bob']];
    }
}

// 具體原型:RowGateway
class RowGateway {
    private DbAdapter $dbAdapter;
    private string $tableName;
    public array $data = [];
    
    public function __construct(DbAdapter $dbAdapter, string $tableName) {
        $this->dbAdapter = $dbAdapter;
        $this->tableName = $tableName;
        // 昂貴操作只在原型建立時做一次
    }
    
    public function initialize(array $rowData): void {
        $this->data = $rowData; // 輕量:每行資料不同
    }
    
    public function save(): void { /* ... */ }
    public function delete(): void { /* ... */ }
    public function refresh(): void { /* ... */ }
}

// 使用原型的 Repository
class UserRepository {
    private DbAdapter $dbAdapter;
    private RowGateway $rowGatewayPrototype;
    
    public function __construct(DbAdapter $dbAdapter, RowGateway $prototype = null) {
        $this->dbAdapter = $dbAdapter;
        $this->rowGatewayPrototype = $prototype ?? new RowGateway($dbAdapter, 'user');
    }
    
    public function getUsers(): array {
        $rows = [];
        foreach ($this->dbAdapter->fetchAllFromTable('user') as $rowData) {
            $row = clone $this->rowGatewayPrototype; // 關鍵:clone 原型
            $row->initialize($rowData);              // 初始化差異資料
            $rows[] = $row;
        }
        return $rows;
    }
}

// 使用示範
$db = new DbAdapter();
$repo = new UserRepository($db);
$users = $repo->getUsers(); // 內部自動 clone + initialize
foreach ($users as $user) {
    echo $user->data['name'] . PHP_EOL;
}

進階:擴充原型支援多資料庫讀寫分離

class ReadWriteRowGateway extends RowGateway {
    private DbAdapter $readDbAdapter;
    
    public function __construct(DbAdapter $read, DbAdapter $write, string $table) {
        $this->readDbAdapter = $read;
        parent::__construct($write, $table); // 寫入用主庫
    }
    
    public function refresh(): void {
        // 讀取用從庫
        $data = $this->readDbAdapter->fetchOne($this->tableName, $this->data['id']);
        $this->initialize($data);
    }
}

// 注入擴充原型
$repo = new UserRepository(
    $writeDb,
    new ReadWriteRowGateway($readDb, $writeDb, 'user')
);

原型模式 vs 其他創建型模式

模式適用場景PHP 典型用法
原型大量相似物件、初始化昂貴clone + initialize()
工廠方法依條件建立不同子類別Factory::create($type)
建造者複雜物件分步建構$builder->setX()->setY()->build()
單例全域唯一實例static getInstance()

注意事項

  • clone 預設為**淺拷貝**,包含資源/物件屬性時需實作 __clone() 深拷貝
  • 原型物件建立後**不可變更共用狀態**,否則影響所有克隆體
  • PHP 8.1+ 可搭配 readonly 屬性確保不可變性

總結

原型模式在 PHP 中的精髓:**建構子做重活、initialize 做輕活、clone 做複製**。Zend Framework 的 ResultSetPrototype 是教科書級實作,掌握此模式可顯著優化大量物件建立場景(報表生成、批次匯出、多行資料映射)的效能與記憶體表現。

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