三十的博客

PHP 常见设计模式

发布时间
最后更新
阅读量 加载中...

单例模式

适用场景:需要确保一个类只有一个实例,并提供全局访问点(如数据库连接、配置管理)

php
class Database
{
    private static $instance = null;
    private $connection;

    private function __construct()
    {
        $this->connection = new PDO('mysql:host=localhost;dbname=test', 'root', '');
    }

    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function query($sql)
    {
        return $this->connection->query($sql);
    }

    // 防止克隆
    private function __clone(){}
}

// 使用
$db1 = Database::getInstance();
$db2 = Database::getInstance();
var_dump($db1 === $db2); // 输出 true,确保唯一实例

工厂模式

适用场景:将对象的创建与使用分离,便于扩展(如创建不同类型的支付方式)

php
interface Payment
{
    public function pay($amount);
}

class CreditCardPayment implements Payment
{
    public function pay($amount)
    {
        echo "用信用卡支付: {$amount}\n";
    }
}

class PayPalPayment implements Payment
{
    public function pay($amount)
    {
        echo "用PayPal支付: {$amount}\n";
    }
}

class PaymentFactory
{
    private static $paymentMap = [
        'credit_card' => CreditCardPayment::class,
        'paypal' => PayPalPayment::class,
    ];

    public static function register(string $type, string $paymentClass): void
    {
        // class_implements 返回指定的类实现的所有接口
        if (!in_array(Payment::class, class_implements($paymentClass), true)) {
            throw new InvalidArgumentException("{$paymentClass} 必须实现 Payment 接口");
        }
        self::$paymentMap[$type] = $paymentClass;
    }

    public static function create(string $type): Payment
    {
        if (!isset(self::$paymentMap[$type])) {
            throw new InvalidArgumentException("未注册的支付类型: {$type}");
        }

        $paymentClass = self::$paymentMap[$type];
        return new $paymentClass();
    }
}

// 使用示例
PaymentFactory::register('paypal', PayPalPayment::class);
$payment = PaymentFactory::create('paypal');
$payment->pay(100);

// 扩展新支付方式示例
class WeChatPayment implements Payment
{
    public function pay($amount)
    {
        echo "用微信支付: {$amount}\n";
    }
}

// 注册新类型无需修改工厂类
PaymentFactory::register('wechat', WeChatPayment::class);
$wxPayment = PaymentFactory::create('wechat');
$wxPayment->pay(200);

策略模式

适用场景:定义一系列算法,封装起来并使它们可互换(如不同的折扣算法)

php
// 策略接口
interface DiscountStrategy
{
    public function calculate(float $amount): float;
}

// 具体策略类:无折扣
class NoDiscount implements DiscountStrategy
{
    public function calculate(float $amount): float
    {
        return $amount;
    }
}

// 具体策略类:固定折扣
class FixedDiscount implements DiscountStrategy
{
    private $discount;

    public function __construct(float $discount)
    {
        $this->discount = $discount;
    }

    public function calculate(float $amount): float
    {
        return max($amount - $this->discount, 0);
    }
}

// 具体策略类:百分比折扣
class PercentageDiscount implements DiscountStrategy
{
    private $percentage;

    public function __construct(float $percentage)
    {
        $this->percentage = $percentage;
    }

    public function calculate(float $amount): float
    {
        return $amount * (1 - $this->percentage / 100);
    }
}

// 上下文类
class OrderContext
{
    private $strategy;

    public function __construct(DiscountStrategy $strategy)
    {
        $this->strategy = $strategy;
    }

    public function setStrategy(DiscountStrategy $strategy): void
    {
        $this->strategy = $strategy;
    }

    public function checkout(float $amount): float
    {
        return $this->strategy->calculate($amount);
    }
}

// 使用示例
$order = new OrderContext(new NoDiscount());
echo "原价100元,无折扣:" . $order->checkout(100) . "元\n"; // 100元

$order->setStrategy(new FixedDiscount(20));
echo "原价100元,立减20:" . $order->checkout(100) . "元\n"; // 80元

$order->setStrategy(new PercentageDiscount(30));
echo "原价100元,7折:" . $order->checkout(100) . "元\n"; // 70元

// 动态切换策略
$order->setStrategy(new FixedDiscount(50));
echo "原价80元,立减50:" . $order->checkout(80) . "元\n"; // 30元

工厂模式 Vs 策略模式

关键区别说明:

在实际开发中,两种模式可以结合使用:

php
// 策略工厂
class StrategyFactory {
    public static function create(string $type): DiscountStrategy {
        return match($type) {
            'fixed' => new FixedDiscount(20),
            'percentage' => new PercentageDiscount(30),
            default => new NoDiscount(),
        };
    }
}

// 使用组合
$strategy = StrategyFactory::create('percentage');
$order = new OrderContext($strategy);
echo "工厂+策略组合:".$order->checkout(100); // 70元

设计选择建议:

观察者模式

适用场景:当一个对象状态改变时,自动通知依赖它的其他对象(如用户注册后发送邮件和短信)

php
// 观察者接口(订阅者)
interface OrderObserver
{
    public function update(OrderSubject $subject, string $eventType, $data = null);
}

// 被观察对象接口(发布者)
interface OrderSubject
{
    // 订阅
    public function attach(OrderObserver $observer, string $eventType = '*'): void;

    // 取消订阅
    public function detach(OrderObserver $observer, string $eventType = '*'): void;

    // 通知
    public function notify(string $eventType, $data = null): void;
}

// 具体订单类
class Order implements OrderSubject
{
    private $observers = [];
    private $orderStatus;
    private $orderNumber;

    public function __construct(string $orderNumber)
    {
        $this->orderNumber = $orderNumber;
        $this->orderStatus = 'created';
    }

    // 实现订阅方法(支持按事件类型订阅)
    public function attach(OrderObserver $observer, string $eventType = '*'): void
    {
        $this->observers[$eventType][] = $observer;
    }

    // 实现取消订阅
    public function detach(OrderObserver $observer, string $eventType = '*'): void
    {
        if ($eventType === '*') {
            foreach ($this->observers as $type => $observers) {
                $this->removeObserver($type, $observer);
            }
        } else {
            $this->removeObserver($eventType, $observer);
        }
    }

    // 移除观察者
    private function removeObserver(string $eventType, OrderObserver $observer): void
    {
        if (isset($this->observers[$eventType])) {
            $key = array_search($observer, $this->observers[$eventType], true);
            if ($key !== false) {
                unset($this->observers[$eventType][$key]);
            }
        }
    }

    // 状态变更通知
    public function notify(string $eventType, $data = null): void
    {
        // 通知特定事件类型的观察者
        if (isset($this->observers[$eventType])) {
            foreach ($this->observers[$eventType] as $observer) {
                $observer->update($this, $eventType, $data);
            }
        }

        // 通知全局观察者(监听所有事件)
        if (isset($this->observers['*'])) {
            foreach ($this->observers['*'] as $observer) {
                $observer->update($this, $eventType, $data);
            }
        }
    }

    // 业务方法:支付订单
    public function pay(): void
    {
        $this->orderStatus = 'paid';
        $this->notify('payment_success', [
            'amount' => 100,
            'paid_at' => date('Y-m-d H:i:s')
        ]);
    }

    // 业务方法:发货
    public function ship(): void
    {
        $this->orderStatus = 'shipped';
        $this->notify('order_shipped', [
            'tracking_number' => 'SF123456789',
            'shipped_at' => date('Y-m-d H:i:s')
        ]);
    }

    public function getStatus(): string
    {
        return $this->orderStatus;
    }

    public function getOrderNumber(): string
    {
        return $this->orderNumber;
    }
}

// 具体观察者:邮件通知服务
class EmailNotifier implements OrderObserver
{
    public function update(OrderSubject $subject, string $eventType, $data = null)
    {
        $order = $subject->getOrderNumber();
        switch ($eventType) {
            case 'payment_success':
                echo "[邮件通知] 订单 {$order} 支付成功,金额:{$data['amount']}\n";
                break;
            case 'order_shipped':
                echo "[邮件通知] 订单 {$order} 已发货,运单号:{$data['tracking_number']}\n";
                break;
        }
    }
}

// 具体观察者:短信通知服务
class SMSNotifier implements OrderObserver
{
    public function update(OrderSubject $subject, string $eventType, $data = null)
    {
        $order = $subject->getOrderNumber();
        if ($eventType === 'payment_success') {
            echo "[短信通知] 订单 {$order} 支付成功,感谢您的购买!\n";
        }
    }
}

// 具体观察者:数据分析服务(监听所有事件)
class AnalyticsService implements OrderObserver
{
    public function update(OrderSubject $subject, string $eventType, $data = null)
    {
        $order = $subject->getOrderNumber();
        echo "[数据分析] 记录事件:{$eventType} - 订单 {$order}\n";
    }
}

// 使用示例
$order = new Order('ORDER_20230721_001');

// 创建观察者
$emailNotifier = new EmailNotifier();
$smsNotifier = new SMSNotifier();
$analytics = new AnalyticsService();

// 订阅事件
$order->attach($emailNotifier, 'payment_success');
$order->attach($emailNotifier, 'order_shipped');
$order->attach($smsNotifier, 'payment_success');
$order->attach($analytics); // 监听所有事件

// 执行支付操作
$order->pay();
/* 输出:
[邮件通知] 订单 ORDER_20230721_001 支付成功,金额:100元
[短信通知] 订单 ORDER_20230721_001 支付成功,感谢您的购买!
[数据分析] 记录事件:payment_success - 订单 ORDER_20230721_001
*/

// 执行发货操作
$order->ship();
/* 输出:
[邮件通知] 订单 ORDER_20230721_001 已发货,运单号:SF123456789
[数据分析] 记录事件:order_shipped - 订单 ORDER_20230721_001
*/

// 取消短信服务的支付成功通知
$order->detach($smsNotifier, 'payment_success');

// 再次支付不会触发短信通知
$order->pay();
/* 输出:
[邮件通知] 订单 ORDER_20230721_001 支付成功,金额:100元
[数据分析] 记录事件:payment_success - 订单 ORDER_20230721_001
*/

装饰器模式

适用场景:动态地为对象添加职责(如给咖啡添加不同的配料)

php
// 基础组件接口
interface CoffeeInterface
{
    public function getCost(): float;

    public function getDescription(): string;
}

// 具体组件:基础咖啡
class SimpleCoffee implements CoffeeInterface
{
    public function getCost(): float
    {
        return 5.0; // 基础价格5元
    }

    public function getDescription(): string
    {
        return "普通咖啡";
    }
}

// 装饰器抽象类
abstract class CoffeeDecorator implements CoffeeInterface
{
    protected $coffee;

    public function __construct(CoffeeInterface $coffee)
    {
        $this->coffee = $coffee;
    }

    abstract public function getCost(): float;

    abstract public function getDescription(): string;
}

// 具体装饰器:牛奶
class MilkDecorator extends CoffeeDecorator
{
    public function getCost(): float
    {
        return $this->coffee->getCost() + 1.0; // 加牛奶1元
    }

    public function getDescription(): string
    {
        return $this->coffee->getDescription() . " + 牛奶";
    }
}

// 具体装饰器:糖
class SugarDecorator extends CoffeeDecorator
{
    public function getCost(): float
    {
        return $this->coffee->getCost() + 0.5; // 加糖0.5元
    }

    public function getDescription(): string
    {
        return $this->coffee->getDescription() . " + 糖";
    }
}

// 具体装饰器:奶油
class CreamDecorator extends CoffeeDecorator
{
    public function getCost(): float
    {
        return $this->coffee->getCost() + 2.0; // 加奶油2元
    }

    public function getDescription(): string
    {
        return $this->coffee->getDescription() . " + 奶油";
    }
}

// 客户端使用示例
$coffee = new SimpleCoffee();
echo $coffee->getDescription() . " 价格:" . $coffee->getCost() . "元\n";
// 输出:普通咖啡 价格:5元

// 加牛奶
$coffeeWithMilk = new MilkDecorator($coffee);
echo $coffeeWithMilk->getDescription() . " 价格:" . $coffeeWithMilk->getCost() . "元\n";
// 输出:普通咖啡 + 牛奶 价格:6元

// 加牛奶和糖
$coffeeWithMilkAndSugar = new SugarDecorator($coffeeWithMilk);
echo $coffeeWithMilkAndSugar->getDescription() . " 价格:" . $coffeeWithMilkAndSugar->getCost() . "元\n";
// 输出:普通咖啡 + 牛奶 + 糖 价格:6.5元

// 加奶油、牛奶和糖
$coffeeWithAll = new CreamDecorator($coffeeWithMilkAndSugar);
echo $coffeeWithAll->getDescription() . " 价格:" . $coffeeWithAll->getCost() . "元\n";
// 输出:普通咖啡 + 牛奶 + 糖 + 奶油 价格:8.5元

// 动态组合装饰器(奶油+糖)
$customCoffee = new CreamDecorator(new SugarDecorator(new SimpleCoffee()));
echo $customCoffee->getDescription() . " 价格:" . $customCoffee->getCost() . "元\n";
// 输出:普通咖啡 + 糖 + 奶油 价格:7.5元
#Php