设计模式是软件开发中经过反复验证的、解决特定问题的最佳实践。掌握常用的设计模式,能帮助我们写出更优雅、更可维护、更易扩展的代码。今天就来分享PHP中最常用的三种设计模式:单例模式、工厂模式、观察者模式,并结合实际代码讲解它们的应用场景和实现方法。
为什么要学习设计模式
很多初学者觉得设计模式很抽象,很难理解,在实际开发中也用不上。但是当你参与过几个中大型项目,维护过一些"祖传代码"后,就会发现设计模式的重要性。
- 提高代码可维护性:设计模式能让代码结构更清晰,职责更单一,更容易理解和维护。
- 提高代码可扩展性:设计模式遵循"开闭原则"(对扩展开放,对修改关闭),能让我们在不修改现有代码的情况下扩展功能。
- 提高开发效率:设计模式是前人经验的总结,遇到类似问题时直接套用,能少走弯路。
- 方便团队协作:设计模式有统一的术语和结构,团队成员之间沟通更顺畅。
当然,设计模式不是银弹,不能为了用模式而用模式。要根据实际问题选择合适的设计模式,简单的问题用简单的方法解决,不要过度设计。
一、单例模式(Singleton)
定义:单例模式确保一个类只有一个实例,并提供一个全局访问点。
应用场景:
- 数据库连接:避免重复创建数据库连接,节省资源
- 配置管理:全局只有一个配置对象,统一管理配置
- 日志记录:全局只有一个日志对象,统一记录日志
- 缓存管理:全局只有一个缓存对象,统一管理缓存
PHP实现:
<?php
class Database
{
private static $instance = null;
private $pdo;
// 私有构造函数,禁止外部new
private function __construct()
{
$this->pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8mb4', 'root', 'password');
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
// 私有克隆函数,禁止克隆
private function __clone() {}
// 私有反序列化函数,禁止反序列化
private function __wakeup() {}
// 全局访问点
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function getConnection()
{
return $this->pdo;
}
public function query($sql)
{
return $this->pdo->query($sql);
}
}
// 使用
$db = Database::getInstance();
$result = $db->query("SELECT * FROM users");要点:
- 构造函数、克隆函数、反序列化函数都设为private,禁止外部创建实例
- 用静态变量保存唯一实例
- 提供静态方法getInstance()获取实例,首次调用时创建实例
- 单例模式是"懒加载"的,只有在第一次使用时才创建实例
优缺点:
- 优点:节省资源,避免重复创建对象,全局统一访问
- 缺点:扩展性差,不易测试,违反单一职责原则(既负责创建对象,又负责业务逻辑)
二、工厂模式(Factory)
定义:工厂模式提供了一种创建对象的最佳方式,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
工厂模式分为简单工厂、工厂方法、抽象工厂三种,这里介绍最常用的简单工厂和工厂方法。
1. 简单工厂模式
应用场景:根据不同的参数创建不同的对象,将对象创建逻辑集中管理。
PHP实现:
<?php
// 产品接口
interface Logger
{
public function log($message);
}
// 具体产品:文件日志
class FileLogger implements Logger
{
public function log($message)
{
file_put_contents('app.log', date('Y-m-d H:i:s') . ' ' . $message . "\n", FILE_APPEND);
}
}
// 具体产品:数据库日志
class DatabaseLogger implements Logger
{
public function log($message)
{
// 写入数据库
echo "写入数据库日志:$message\n";
}
}
// 具体产品:邮件日志
class EmailLogger implements Logger
{
public function log($message)
{
// 发送邮件
echo "发送邮件日志:$message\n";
}
}
// 简单工厂
class LoggerFactory
{
public static function create($type)
{
switch ($type) {
case 'file':
return new FileLogger();
case 'database':
return new DatabaseLogger();
case 'email':
return new EmailLogger();
default:
throw new InvalidArgumentException("不支持的日志类型:$type");
}
}
}
// 使用
$logger = LoggerFactory::create('file');
$logger->log('这是一条日志');优缺点:
- 优点:将对象创建逻辑集中管理,客户端不需要知道具体创建细节
- 缺点:违反开闭原则,新增产品类型需要修改工厂类
2. 工厂方法模式
定义:定义一个创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
PHP实现:
<?php
// 产品接口
interface Payment
{
public function pay($amount);
}
// 具体产品:支付宝支付
class AlipayPayment implements Payment
{
public function pay($amount)
{
echo "支付宝支付:$amount 元\n";
}
}
// 具体产品:微信支付
class WechatPayment implements Payment
{
public function pay($amount)
{
echo "微信支付:$amount 元\n";
}
}
// 具体产品:银联支付
class UnionpayPayment implements Payment
{
public function pay($amount)
{
echo "银联支付:$amount 元\n";
}
}
// 工厂接口
interface PaymentFactory
{
public function createPayment();
}
// 具体工厂:支付宝工厂
class AlipayFactory implements PaymentFactory
{
public function createPayment()
{
return new AlipayPayment();
}
}
// 具体工厂:微信工厂
class WechatFactory implements PaymentFactory
{
public function createPayment()
{
return new WechatPayment();
}
}
// 具体工厂:银联工厂
class UnionpayFactory implements PaymentFactory
{
public function createPayment()
{
return new UnionpayPayment();
}
}
// 使用
$factory = new AlipayFactory();
$payment = $factory->createPayment();
$payment->pay(100);优缺点:
- 优点:符合开闭原则,新增产品类型只需要新增对应的工厂类,不需要修改现有代码
- 缺点:类的数量增多,系统复杂度增加
三、观察者模式(Observer)
定义:观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
应用场景:
- 事件监听:用户注册后,发送邮件、短信、站内信
- 消息通知:文章发布后,通知订阅者
- 数据同步:数据变更后,同步到缓存、搜索引擎
- 插件系统:核心功能触发事件,插件监听事件执行自定义逻辑
PHP实现:
<?php
// 观察者接口
interface Observer
{
public function update($event, $data);
}
// 被观察者(主题)接口
interface Subject
{
public function attach(Observer $observer);
public function detach(Observer $observer);
public function notify($event, $data);
}
// 具体被观察者:用户注册
class UserRegister implements Subject
{
private $observers = [];
public function attach(Observer $observer)
{
$this->observers[] = $observer;
}
public function detach(Observer $observer)
{
$key = array_search($observer, $this->observers);
if ($key !== false) {
unset($this->observers[$key]);
}
}
public function notify($event, $data)
{
foreach ($this->observers as $observer) {
$observer->update($event, $data);
}
}
public function register($username, $email)
{
// 用户注册逻辑
echo "用户 $username 注册成功\n";
// 触发事件,通知所有观察者
$this->notify('user.register', ['username' => $username, 'email' => $email]);
}
}
// 具体观察者:发送邮件
class EmailObserver implements Observer
{
public function update($event, $data)
{
if ($event === 'user.register') {
echo "发送邮件到 {$data['email']}:欢迎注册\n";
}
}
}
// 具体观察者:发送短信
class SmsObserver implements Observer
{
public function update($event, $data)
{
if ($event === 'user.register') {
echo "发送短信:欢迎注册,{$data['username']}\n";
}
}
}
// 具体观察者:记录日志
class LogObserver implements Observer
{
public function update($event, $data)
{
echo "记录日志:事件 $event,数据 " . json_encode($data) . "\n";
}
}
// 使用
$userRegister = new UserRegister();
$userRegister->attach(new EmailObserver());
$userRegister->attach(new SmsObserver());
$userRegister->attach(new LogObserver());
$userRegister->register('张三', 'zhangsan@example.com');PHP内置支持: PHP提供了SplSubject和SplObserver接口,可以直接使用:
<?php
class User implements SplSubject
{
private $observers = [];
private $name;
public function attach(SplObserver $observer)
{
$this->observers[] = $observer;
}
public function detach(SplObserver $observer)
{
$key = array_search($observer, $this->observers);
if ($key !== false) {
unset($this->observers[$key]);
}
}
public function notify()
{
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
public function setName($name)
{
$this->name = $name;
$this->notify();
}
public function getName()
{
return $this->name;
}
}
class UserLogger implements SplObserver
{
public function update(SplSubject $subject)
{
echo "用户名更新为:" . $subject->getName() . "\n";
}
}优缺点:
- 优点:解耦被观察者和观察者,支持广播通信,符合开闭原则
- 缺点:观察者过多时通知耗时,观察者之间可能存在循环依赖
设计模式原则
学习设计模式,更重要的是理解背后的设计原则:
- 单一职责原则:一个类只负责一个功能领域中的相应职责
- 开闭原则:对扩展开放,对修改关闭
- 里氏替换原则:子类可以替换父类出现在任何地方
- 依赖倒置原则:依赖抽象,不依赖具体实现
- 接口隔离原则:使用多个小接口,不使用一个大接口
- 迪米特法则:一个对象应当对其他对象有尽可能少的了解
总结
单例模式、工厂模式、观察者模式是PHP开发中最常用的三种设计模式,掌握它们能大大提高我们的代码质量。
- 单例模式:确保一个类只有一个实例,适用于数据库连接、配置管理等场景
- 工厂模式:将对象创建逻辑封装起来,适用于需要根据不同条件创建不同对象的场景
- 观察者模式:实现对象间的一对多依赖,适用于事件监听、消息通知等场景
设计模式不是银弹,要根据实际问题选择合适的模式,不要为了用模式而用模式。理解设计模式背后的设计原则,比死记硬背模式更重要。
希望这篇文章能帮助大家更好地理解和使用设计模式,写出更高质量的代码。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录