PHP 中的设计模式实战:策略模式、观察者模式与装饰器模式

设计模式是软件开发中经过验证的解决方案模板,它们帮助我们以更优雅、可维护的方式组织代码。在 PHP 开发中,策略模式、观察者模式和装饰器模式是三种最常用且实用的设计模式。本文将通过实际场景和代码示例,深入探讨这三种模式的应用。

一、策略模式(Strategy Pattern)

1.1 什么是策略模式

策略模式定义了一系列算法,将每个算法封装起来,并使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户端。

1.2 实际应用场景

假设我们正在开发一个电商系统,需要根据不同的用户类型计算商品折扣价格。普通用户享受 95 折,VIP 用户享受 8 折,超级 VIP 享受 7 折。

1.3 代码实现

首先定义策略接口:

<?php

interface DiscountStrategy
{
    public function calculate(float $price): float;
}

然后实现具体的折扣策略:

class NormalUserDiscount implements DiscountStrategy
{
    public function calculate(float $price): float
    {
        return $price * 0.95;
    }
}

class VipUserDiscount implements DiscountStrategy
{
    public function calculate(float $price): float
    {
        return $price * 0.80;
    }
}

class SuperVipDiscount implements DiscountStrategy
{
    public function calculate(float $price): float
    {
        return $price * 0.70;
    }
}

创建上下文类来使用策略:

class PriceCalculator
{
    private DiscountStrategy $strategy;

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

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

使用示例:

$calculator = new PriceCalculator();

$calculator->setStrategy(new VipUserDiscount());
echo $calculator->getFinalPrice(1000); // 输出 800

$calculator->setStrategy(new SuperVipDiscount());
echo $calculator->getFinalPrice(1000); // 输出 700

1.4 优势总结

  • 开闭原则:新增折扣类型无需修改现有代码
  • 消除条件语句:避免大量的 if-else 或 switch-case
  • 算法独立:每个策略类职责单一,便于测试

二、观察者模式(Observer Pattern)

2.1 什么是观察者模式

观察者模式定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会收到通知并自动更新。

2.2 实际应用场景

在用户注册系统中,注册成功后需要执行多个操作:发送欢迎邮件、记录日志、发放新人优惠券。使用观察者模式可以将这些操作解耦。

2.3 代码实现

首先定义观察者接口和主题接口:

interface Observer
{
    public function update(string $event, array $data): void;
}

interface Subject
{
    public function attach(Observer $observer): void;
    public function detach(Observer $observer): void;
    public function notify(string $event, array $data): void;
}

实现具体的主题类:

class UserRegistration implements Subject
{
    private array $observers = [];

    public function attach(Observer $observer): void
    {
        $this->observers[] = $observer;
    }

    public function detach(Observer $observer): void
    {
        $index = array_search($observer, $this->observers, true);
        if ($index !== false) {
            unset($this->observers[$index]);
            $this->observers = array_values($this->observers);
        }
    }

    public function notify(string $event, array $data): void
    {
        foreach ($this->observers as $observer) {
            $observer->update($event, $data);
        }
    }

    public function register(string $username, string $email): void
    {
        // 执行注册逻辑
        echo "用户 {$username} 注册成功\n";

        // 通知所有观察者
        $this->notify('user.registered', [
            'username' => $username,
            'email' => $email,
        ]);
    }
}

实现具体的观察者:

class WelcomeEmailObserver implements Observer
{
    public function update(string $event, array $data): void
    {
        if ($event === 'user.registered') {
            echo "发送欢迎邮件到 {$data['email']}\n";
        }
    }
}

class LogObserver implements Observer
{
    public function update(string $event, array $data): void
    {
        echo "记录日志:事件 {$event},用户 {$data['username']}\n";
    }
}

class CouponObserver implements Observer
{
    public function update(string $event, array $data): void
    {
        if ($event === 'user.registered') {
            echo "为用户 {$data['username']} 发放新人优惠券\n";
        }
    }
}

使用示例:

$registration = new UserRegistration();
$registration->attach(new WelcomeEmailObserver());
$registration->attach(new LogObserver());
$registration->attach(new CouponObserver());

$registration->register('张三', 'zhangsan@example.com');

2.4 优势总结

  • 松耦合:主题与观察者之间互不依赖具体实现
  • 可扩展性:新增观察者无需修改主题代码
  • 符合开闭原则:对扩展开放,对修改关闭

三、装饰器模式(Decorator Pattern)

3.1 什么是装饰器模式

装饰器模式允许向一个现有的对象添加新的功能,同时不改变其结构。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供额外的功能。

3.2 实际应用场景

在日志系统中,我们可能需要将日志输出到不同地方(文件、数据库、控制台),并且可能需要添加时间戳、格式化等额外处理。

3.3 代码实现

定义日志接口:

interface Logger
{
    public function log(string $message): string;
}

基础实现:

class FileLogger implements Logger
{
    public function log(string $message): string
    {
        return "[文件日志] {$message}";
    }
}

class DatabaseLogger implements Logger
{
    public function log(string $message): string
    {
        return "[数据库日志] {$message}";
    }
}

创建装饰器基类:

abstract class LoggerDecorator implements Logger
{
    protected Logger $logger;

    public function __construct(Logger $logger)
    {
        $this->logger = $logger;
    }

    public function log(string $message): string
    {
        return $this->logger->log($message);
    }
}

实现具体装饰器:

class TimestampDecorator extends LoggerDecorator
{
    public function log(string $message): string
    {
        $timestamp = date('Y-m-d H:i:s');
        return $this->logger->log("[{$timestamp}] {$message}");
    }
}

class JsonFormatDecorator extends LoggerDecorator
{
    public function log(string $message): string
    {
        $formatted = json_encode(['message' => $message, 'level' => 'info']);
        return $this->logger->log($formatted);
    }
}

class UppercaseDecorator extends LoggerDecorator
{
    public function log(string $message): string
    {
        return $this->logger->log(strtoupper($message));
    }
}

使用示例:

// 基础日志
$logger = new FileLogger();
echo $logger->log("系统启动") . "\n";

// 添加时间戳装饰
$logger = new TimestampDecorator(new FileLogger());
echo $logger->log("系统启动") . "\n";

// 叠加多个装饰器
$logger = new UppercaseDecorator(
    new TimestampDecorator(
        new JsonFormatDecorator(
            new DatabaseLogger()
        )
    )
);
echo $logger->log("用户登录") . "\n";

3.4 优势总结

  • 灵活性:可以动态地添加或删除功能
  • 组合优于继承:避免类爆炸问题
  • 单一职责:每个装饰器只关注一个功能

四、三种模式的对比与选择

模式 核心思想 适用场景
策略模式 封装可互换的算法 多种业务规则切换、支付方式选择
观察者模式 一对多依赖通知 事件系统、消息推送、钩子机制
装饰器模式 动态添加功能 日志处理、中间件、数据流处理

五、总结

设计模式不是银弹,而是帮助我们更好地组织代码的工具。在实际开发中:

  1. 策略模式适合处理同一问题的不同算法实现,避免冗长的条件判断
  2. 观察者模式适合构建事件驱动架构,实现模块间的松耦合
  3. 装饰器模式适合在不修改原有类的前提下,灵活地扩展功能

掌握这三种模式,能够显著提升 PHP 代码的可维护性和可扩展性。建议在实际项目中多加练习,根据具体场景灵活运用,而非为了使用模式而使用模式。

未经允许不得转载:任鹏个人博客 » PHP 中的设计模式实战:策略模式、观察者模式与装饰器模式

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏