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

PHP 設計模式入門:工廠模式解耦創建邏輯的藝術

     編輯:PHP 相關知識

前言:創建邏輯不應污染業務代碼

當 new User() 散落在控制器、服務、命令各處,需求變更(如改用數據庫用戶、添加緩存、切換 API 客戶端)將引發連鎖修改。工廠模式將對象創建封裝、集中、參數化,實現「依賴抽象、不依賴具體」。本文從簡單工廠、工廠方法、抽象工廠三個層次,配合 PHP 介面、類型聲明、容器綁定,構建可擴展的創建體系。

一、簡單工廠:靜態方法封裝創建

<?php
// 1. 產品介面
interface UserInterface {
    public function getName(): string;
    public function getEmail(): string;
}

// 2. 具體產品
class DatabaseUser implements UserInterface {
    public function __construct(private int $id) {}
    public function getName(): string { return "DB_User_{$this->id}"; }
    public function getEmail(): string { return "user{$this->id}@db.com"; }
}

class ApiUser implements UserInterface {
    public function __construct(private string $uuid) {}
    public function getName(): string { return "API_User_{$this->uuid}"; }
    public function getEmail(): string { return "api-{$this->uuid}@example.com"; }
}

// 3. 簡單工廠
class UserFactory {
    public static function create(string $type, string $identifier): UserInterface {
        return match ($type) {
            'database' => new DatabaseUser((int)$identifier),
            'api'      => new ApiUser($identifier),
            default    => throw new InvalidArgumentException("Unknown user type: $type"),
        };
    }
}

// 4. 使用:業務代碼零 new
$user = UserFactory::create('database', '123');
echo $user->getName(); // DB_User_123

優缺點

  • ✅ 簡單、集中創建邏輯
  • ❌ 違反開閉原則:添加新類型需修改工廠類
  • ❌ 靜態方法難以 Mock/替換

二、工廠方法:多態延遲創建決策

<?php
// 1. 抽象工廠介面
interface UserFactoryInterface {
    public function createUser(string $identifier): UserInterface;
}

// 2. 具體工廠
class DatabaseUserFactory implements UserFactoryInterface {
    public function createUser(string $identifier): UserInterface {
        return new DatabaseUser((int)$identifier);
    }
}

class ApiUserFactory implements UserFactoryInterface {
    public function createUser(string $identifier): UserInterface {
        return new ApiUser($identifier);
    }
}

// 3. 客戶端依賴抽象工廠
class UserService {
    public function __construct(private UserFactoryInterface $factory) {}
    
    public function register(string $type, string $id): UserInterface {
        // 這裡可根據類型選擇工廠,或由容器注入特定工廠
        return $this->factory->createUser($id);
    }
}

// 4. 容器綁定 (Laravel 風格)
$container->bind(UserFactoryInterface::class, fn() => new DatabaseUserFactory());
// 或運行時動態
$factory = match ($config['user_source']) {
    'db' => new DatabaseUserFactory(),
    'api' => new ApiUserFactory(),
};
$service = new UserService($factory);

優勢

  • ✅ 符合開閉原則:新增類型只需新增工廠類
  • ✅ 依賴倒置:業務依賴介面,便於測試替換
  • ✅ 支持運行時動態切換實現

三、抽象工廠:產品族創建

當需要創建相關/依賴的對象族(如:不同數據庫的 Connection + Statement + Transaction):

<?php
// 1. 產品族介面
interface ConnectionInterface { public function query(string $sql); }
interface StatementInterface { public function execute(array $params); }
interface TransactionInterface { public function commit(); public function rollback(); }

// 2. 抽象工廠
interface DatabaseFactoryInterface {
    public function createConnection(): ConnectionInterface;
    public function createStatement(): StatementInterface;
    public function createTransaction(): TransactionInterface;
}

// 3. MySQL 族
class MySqlConnection implements ConnectionInterface { /* ... */ }
class MySqlStatement implements StatementInterface { /* ... */ }
class MySqlTransaction implements TransactionInterface { /* ... */ }

class MySqlFactory implements DatabaseFactoryInterface {
    public function createConnection(): ConnectionInterface { return new MySqlConnection(); }
    public function createStatement(): StatementInterface { return new MySqlStatement(); }
    public function createTransaction(): TransactionInterface { return new MySqlTransaction(); }
}

// 4. PostgreSQL 族
class PgSqlFactory implements DatabaseFactoryInterface { /* ... */ }

// 5. 使用
function migrate(DatabaseFactoryInterface $factory) {
    $conn = $factory->createConnection();
    $tx = $factory->createTransaction();
    // ... 使用族內協作對象
}

四、現代 PHP 實踐:容器即工廠

PSR-11 容器本質上就是通用工廠:

<?php
// 綁定
$container->bind(UserInterface::class, fn(ContainerInterface $c) => 
    new DatabaseUser($c->get('config')['db']['default_user_id'])
);

// 解析(自動遞歸解析依賴)
$user = $container->get(UserInterface::class);

// 條件綁定
$container->bind(UserInterface::class, function (ContainerInterface $c) {
    return $c->get('config')['user_source'] === 'api'
        ? new ApiUser($c->get('api_client')->getUserId())
        : new DatabaseUser($c->get('config')['db']['default_user_id']);
});

// 標籤綁定(多實現)
$container->tag(DatabaseUser::class, 'user.implementation');
$container->tag(ApiUser::class, 'user.implementation');

// 獲取所有實現
$implementations = $container->getTagged('user.implementation');

五、工廠模式選型指南

場景推薦模式關鍵點
創建邏輯固定、類型少、無測試需求簡單工廠靜態方法、match 表達式
類型可擴展、需測試、依賴倒置工廠方法介面 + 具體工廠 + 容器綁定
創建相關對象族、多平台適配抽象工廠族介面一致性、配置驅動選擇
現代框架應用容器自動裝配PSR-11、自動解析、標籤、條件綁定

六、避坑指南

  • 工廠過度設計:單一實現無需介面,YAGNI
  • 靜態工廠難測試:改用實例工廠 + 容器注入
  • 創建邏輯外洩:構造函數只做賦值,複雜邏輯進工廠
  • 參數過多:用 DTO/ValueObject 封裝創建參數
  • 循環依賴:工廠不依賴產品,產品不依賴工廠

總結

工廠模式核心是「創建與使用分離」。從 new 關鍵字分散在業務代碼,到集中在工廠方法,再到由容器統一管理生命周期,每一步都在降低耦合、提高可測試性、增強擴展性。現代 PHP 開發中,優先使用容器自動裝配,複雜創建邏輯封裝為工廠類實現介面,由容器綁定介面到工廠。記住:工廠是為了讓業務代碼專注業務,而非為了模式而模式。

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