PHP 设计模式:单例、工厂、观察者实战详解

前言

设计模式不是八股文,它是前人踩坑踩出来的最佳实践。PHP 开发中最常用的三个设计模式:单例、工厂、观察者。本文用最直白的方式讲清楚。

一、单例模式(Singleton)

什么时候用

全局只需要一个实例的对象,比如:

  • 数据库连接
  • 配置管理
  • 日志对象

代码实现

<?php
class Database
{
    private static $instance = null;
    private $conn;

    // 构造函数私有化,防止外部 new
    private function __construct()
    {
        $this->conn = mysqli_connect('localhost', 'user', 'pass', 'db');
    }

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

    // 禁止反序列化
    public function __wakeup()
    {
        throw new Exception("不能反序列化单例");
    }

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

    public function getConnection()
    {
        return $this->conn;
    }
}

// 使用
$db = Database::getInstance();
$conn = $db->getConnection();

为什么要私有化构造函数

如果构造函数是 public 的,外面就能 new Database(),那就不是单例了。

二、工厂模式(Factory)

什么时候用

需要根据不同条件创建不同对象的时候,比如:

  • 根据支付类型创建不同的支付对象(微信、支付宝、银行卡)
  • 根据数据库类型创建不同的数据库对象(MySQL、Redis)

简单工厂

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

class WechatPay implements Payment
{
    public function pay($amount)
    {
        echo "微信支付 {$amount} 元";
    }
}

class Alipay implements Payment
{
    public function pay($amount)
    {
        echo "支付宝支付 {$amount} 元";
    }
}

class PaymentFactory
{
    public static function make($type)
    {
        switch ($type) {
            case 'wechat':
                return new WechatPay();
            case 'alipay':
                return new Alipay();
            default:
                throw new Exception("不支持的支付方式");
        }
    }
}

// 使用
$pay = PaymentFactory::make('wechat');
$pay->pay(100);

工厂模式的好处

  1. 解耦:客户端不需要知道具体怎么创建对象
  2. 易扩展:新增支付方式只需要加一个类和工厂里的一个 case
  3. 统一管理:所有对象的创建都在工厂里,方便维护

三、观察者模式(Observer)

什么时候用

一个对象状态改变时,需要通知多个其他对象,比如:

  • 用户注册后:发邮件、发短信、送积分
  • 订单状态变更后:通知仓库、通知物流、通知客服

代码实现

<?php
// 事件接口
interface Observer
{
    public function update($eventData);
}

// 事件主体
class Event
{
    private $observers = [];

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

    public function trigger($eventData)
    {
        foreach ($this->observers as $observer) {
            $observer->update($eventData);
        }
    }
}

// 具体观察者
class SendEmail implements Observer
{
    public function update($eventData)
    {
        echo "发送邮件给 {$eventData['email']}\n";
    }
}

class SendSms implements Observer
{
    public function update($eventData)
    {
        echo "发送短信给 {$eventData['phone']}\n";
    }
}

class GivePoints implements Observer
{
    public function update($eventData)
    {
        echo "给用户送 100 积分\n";
    }
}

// 使用
$event = new Event();
$event->attach(new SendEmail());
$event->attach(new SendSms());
$event->attach(new GivePoints());

// 用户注册,触发事件
$event->trigger([
    'email' => 'user@example.com',
    'phone' => '13800138000'
]);

观察者模式的好处

  1. 解耦:事件主体不需要知道谁在监听
  2. 易扩展:新增监听者只需要加一个类,不需要改原来的代码
  3. 符合开闭原则:对扩展开放,对修改关闭

四、什么时候用什么模式

场景 用什么模式
全局只需要一个实例 单例
根据条件创建不同对象 工厂
一个动作触发多个后续操作 观察者

五、常见坑

  1. 单例用太多:单例用多了会变成全局变量,代码难维护
  2. 工厂过度设计:简单逻辑就别硬上工厂,会增加复杂度
  3. 观察者滥用:所有操作都做成观察者,调试会很痛苦

总结

设计模式是工具,不是目的。

  • 单例:保证全局只有一个实例
  • 工厂:统一管理对象创建
  • 观察者:一个事件,多个响应

用对了让代码更清晰,用错了让代码更复杂。记住:先解决问题,再考虑模式。


emer 发布于  2026-10-4 10:08