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 策略模式
关键区别说明:
- 目的不同:
- 策略模式:主要解决算法/行为的灵活替换问题
- 工厂模式:主要解决对象创建的解耦问题
- 使用场景:
- 当需要根据不同条件选择不同算法时用策略模式
- 当需要隐藏对象创建细节时用工厂模式
- 生命周期:
- 策略模式中的策略对象通常在运行时可以动态更换
- 工厂模式创建的对象通常在使用时即确定类型
- 组合方式:
- 策略模式通常表现为组合关系(has-a)
- 工厂模式通常表现为创建关系(create-a)
在实际开发中,两种模式可以结合使用:
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元