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

PHP 5 實現多態的兩種核心模式:介面多態與組合模擬多重繼承

     編輯:PHP 綜合知識

PHP 5 引入完整物件模型,雖不支援原生多重繼承,但透過介面與組合可優雅實現多態與程式碼復用。本文以電商折扣系統為例,示範兩種核心模式的實戰應用。

模式一:介面多態

定義契約介面,多種實現類互換,消費端針對介面程式設計而非具體類。

<?php

// 1. 定義契約
interface User
{
    public function getName(): string;
    public function setName(string $name): void;
    public function getDiscount(): float;
}

// 2. 抽象基類封裝共同邏輯
abstract class AbstractUser implements User
{
    protected string $name = '';
    protected float $discount = 1.0;
    protected string $grade = 'Base';

    public function __construct(string $name)
    {
        $this->setName($name);
    }

    public function getName(): string { return $this->name; }
    public function setName(string $name): void { $this->name = $name; }
    public function getDiscount(): float { return $this->discount; }
    public function getGrade(): string { return $this->grade; }
}

// 3. 具體實現
class NormalUser extends AbstractUser
{
    protected float $discount = 1.0;
    protected string $grade = 'Normal';
}

class VipUser extends AbstractUser
{
    protected float $discount = 0.8;
    protected string $grade = 'VIP';
}

class InnerUser extends AbstractUser
{
    protected float $discount = 0.7;
    protected string $grade = 'Internal';
}

// 4. 消費端:結算服務依賴介面 User
class OrderSettlement
{
    public static function calculateFinalPrice(User $user, Product $product, int $quantity): float
    {
        return $user->getDiscount() * $product->getPrice() * $quantity;
    }
}

// 5. 產品介面
interface Product
{
    public function getName(): string;
    public function getPrice(): float;
}

class Book implements Product
{
    public function __construct(private string $name, private float $price = 100) {}

    public function getName(): string { return $this->name; }
    public function getPrice(): float { return $this->price; }
}

// === 使用 ===
$book = new Book('設計模式');
$user = new NormalUser('Tom');
$price = OrderSettlement::calculateFinalPrice($user, $book, 10);

echo "用戶: {$user->getName()} ({$user->getGrade()})\n";
echo "折扣: {$user->getDiscount()}\n";
echo "總價: {$price}\n";

模式二:組合模擬多重繼承

當類別需要複用多個來源的行為 (如:可記錄日誌 + 可序列化 + 可快取),透過「持有介面實例」替代「繼承多個父類」。

<?php

// 行為介面
interface Logger { public function log(string $msg): void; }
interface Cacheable { public function getCacheKey(): string; public function getTtl(): int; }
interface Serializable { public function serialize(): string; public function unserialize(string $data): void; }

// 具體行為實現
class FileLogger implements Logger {
    public function log(string $msg): void { file_put_contents('app.log', $msg . PHP_EOL, FILE_APPEND); }
}

class RedisCache implements Cacheable {
    public function getCacheKey(): string { return 'cache:' . spl_object_id($this); }
    public function getTtl(): int { return 3600; }
}

class JsonSerializer implements Serializable {
    public function serialize(): string { return json_encode($this); }
    public function unserialize(string $data): void { /* ... */ }
}

// 主類別:組合三種行為
class ProductEntity implements Cacheable, Serializable
{
    // 組合:持有介面實例
    private Logger $logger;
    private Cacheable $cache;
    private Serializable $serializer;

    public function __construct(
        private string $name,
        private float $price,
        Logger $logger = null,
        Cacheable $cache = null,
        Serializable $serializer = null
    ) {
        $this->logger = $logger ?? new FileLogger();
        $this->cache = $cache ?? new RedisCache();
        $this->serializer = $serializer ?? new JsonSerializer();
    }

    // 委派給組合物件
    public function log(string $msg): void { $this->logger->log($msg); }
    public function getCacheKey(): string { return $this->cache->getCacheKey(); }
    public function getTtl(): int { return $this->cache->getTtl(); }
    public function serialize(): string { return $this->serializer->serialize(); }
    public function unserialize(string $data): void { $this->serializer->unserialize($data); }

    // 業務方法
    public function applyDiscount(float $rate): void {
        $this->price *= $rate;
        $this->log("Price updated to {$this->price} for {$this->name}");
    }
}

// === 使用 ===
$product = new ProductEntity('筆電', 30000);
$product->applyDiscount(0.9);
echo "Cache Key: {$product->getCacheKey()}\n";
echo "Serialized: {$product->serialize()}\n";

兩種模式對比與選型指南

維度介面多態組合模擬多重繼承
核心目的「是一種」關係:不同實現互換「擁有」關係:複用多組獨立行為
耦合度低 (依賴介面)低 (依賴介面,構造函數注入)
擴展性新增實現類不修改消費端新增行為介面+實現,主類無需修改
適用場景策略模式、支付渠道、用戶分級、存儲驅動橫切關注點 (日誌、快取、序列化、權限、事件)
PHP 8+ 增強interface 可定義 public function 預設實現trait 可替代部分組合場景

最佳實踐總結

  1. 優先用介面定義契約,讓高層模組依賴抽象 (DIP 原則)。
  2. 繼承表達「是一種」 (User → VipUser),組合表達「擁有」 (ProductEntity → Logger)。
  3. 避免深層繼承鏈 (>3 層),改用組合+介面扁平化設計。
  4. PHP 8.1+ 可使用 readonly、Constructor Property Promotion 簡化組合類樣板代碼。

掌握介面多態與組合,即掌握了 PHP 物件導向設計的靈魂,能構建高內聚、低耦合、易測試、可演進的企業級應用架構。

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