引言:为什么PHP开发者需要掌握设计模式
设计模式是软件开发中经过验证的、可复用的解决方案模板。对于PHP开发者来说,掌握设计模式不仅能够提升代码质量,更是从”能写代码”到”写好代码”的关键跨越。很多PHP初学者在项目规模扩大后,常常陷入代码难以维护、扩展性差的困境——这正是设计模式可以解决的问题。
本文将从实际项目出发,深入解析PHP中最常用、最实用的7种设计模式,每个模式都配有完整的实战代码示例和使用场景分析,帮助你真正理解如何在自己的项目中灵活运用。
在开始之前,我们需要明确一个原则:设计模式不是银弹,过度使用反而会增加代码复杂度。我们的目标是在正确的地方用正确的模式,让代码更加清晰、可维护。
创建型模式:对象创建的优雅之道
1. 单例模式(Singleton)
单例模式确保一个类只有一个实例,并提供一个全局访问点。在PHP开发中,最常见的应用场景是数据库连接、配置管理器和日志记录器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 <?php
class DatabaseConnection
{
private static ?DatabaseConnection $instance = null;
private PDO $pdo;
private function __construct(array $config)
{
$dsn = "mysql:host={$config['host']};dbname={$config['dbname']};charset=utf8mb4";
$this->pdo = new PDO($dsn, $config['username'], $config['password'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
}
public static function getInstance(array $config = []): self
{
if (self::$instance === null) {
self::$instance = new self($config);
}
return self::$instance;
}
public function getPdo(): PDO
{
return $this->pdo;
}
// 防止克隆和反序列化
private function __clone() {}
public function __wakeup()
{
throw new \Exception("Cannot unserialize singleton");
}
}
// 使用方法
$db = DatabaseConnection::getInstance($config);
$results = $db->getPdo()->query("SELECT * FROM users")->fetchAll();
?>
需要注意的是,在PHP中单例模式有些争议——因为它引入了全局状态,使得单元测试变得困难。现代PHP框架(如Laravel)更倾向于使用依赖注入容器来管理对象生命周期,而非直接使用单例模式。
2. 工厂方法模式(Factory Method)
当我们需要根据不同的条件创建不同类型的对象时,工厂方法模式是最佳选择。它将在”何时创建什么对象”的逻辑封装到工厂类中,调用者不需要关心具体的实例化过程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 <?php
interface PaymentGateway
{
public function charge(float $amount): array;
}
class AlipayGateway implements PaymentGateway
{
public function charge(float $amount): array
{
// 支付宝支付逻辑
return ['provider' => 'alipay', 'amount' => $amount, 'status' => 'success'];
}
}
class WechatPayGateway implements PaymentGateway
{
public function charge(float $amount): array
{
// 微信支付逻辑
return ['provider' => 'wechat', 'amount' => $amount, 'status' => 'success'];
}
}
class PaymentFactory
{
public static function create(string $type): PaymentGateway
{
return match ($type) {
'alipay' => new AlipayGateway(),
'wechat' => new WechatPayGateway(),
default => throw new \InvalidArgumentException("Unsupported payment type: {$type}"),
};
}
}
// 在业务代码中使用
$gateway = PaymentFactory::create($_POST['payment_method']);
$result = $gateway->charge(99.99);
?>
工厂方法模式的好处在于:新增支付方式只需要添加新的类并修改工厂方法,不用改动业务调用代码,完全符合开闭原则。
结构型模式:构建灵活的对象结构
3. 适配器模式(Adapter)
适配器模式解决的是接口不兼容问题。在PHP项目中,最常见的场景是集成第三方SDK——对方的接口和我们系统预期的不一致,这时候适配器模式就派上用场了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 <?php
// 我们系统预期的接口
interface NotificationInterface
{
public function send(string $to, string $subject, string $body): bool;
}
// 第三方邮件服务SDK(不兼容的接口)
class ThirdPartyMailer
{
public function dispatch(string $recipient, string $content): bool
{
// 调用第三方API
return true;
}
}
// 适配器
class MailAdapter implements NotificationInterface
{
private ThirdPartyMailer $mailer;
public function __construct(ThirdPartyMailer $mailer)
{
$this->mailer = $mailer;
}
public function send(string $to, string $subject, string $body): bool
{
// 将系统接口转换为第三方SDK的调用
$content = "Subject: {$subject}\n\n{$body}";
return $this->mailer->dispatch($to, $content);
}
}
// 使用
$mailer = new MailAdapter(new ThirdPartyMailer());
$mailer->send('user@example.com', 'Welcome!', 'Thank you for registering.');
?>
4. 策略模式(Strategy)
策略模式定义一系列算法,把它们封装起来,并使它们可以相互替换。PHP中一个经典的应用场景是订单运费计算——不同的物流公司有不同的计费规则。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 <?php
interface ShippingStrategy
{
public function calculateCost(float $weight, string $destination): float;
}
class SFExpressStrategy implements ShippingStrategy
{
public function calculateCost(float $weight, string $destination): float
{
// 顺丰计费规则:首重+续重
$base = 12.0;
if ($weight > 1.0) {
$base += ceil($weight - 1) * 5.0;
}
return $base;
}
}
class YundaStrategy implements ShippingStrategy
{
public function calculateCost(float $weight, string $destination): float
{
// 韵达计费规则:按重量阶梯
return $weight * 3.5;
}
}
class OrderShippingCalculator
{
private ShippingStrategy $strategy;
public function __construct(ShippingStrategy $strategy)
{
$this->strategy = $strategy;
}
public function setStrategy(ShippingStrategy $strategy): void
{
$this->strategy = $strategy;
}
public function calculate(float $weight, string $destination): float
{
return $this->strategy->calculateCost($weight, $destination);
}
}
// 使用示例
$calculator = new OrderShippingCalculator(new SFExpressStrategy());
$cost = $calculator->calculate(2.5, '北京');
echo "顺丰运费:{$cost}元";
// 切换为韵达
$calculator->setStrategy(new YundaStrategy());
$cost = $calculator->calculate(2.5, '北京');
echo "韵达运费:{$cost}元";
?>
策略模式的精髓在于”组合优于继承”——通过组合不同的策略对象,我们可以在运行时灵活切换算法,而不需要写一堆if-else判断。
行为型模式:优化对象间的职责分配
5. 观察者模式(Observer)
观察者模式定义了对象之间的一对多依赖关系,当一个对象状态发生变化时,所有依赖它的对象都会得到通知。在PHP中,这是事件驱动架构的基础。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 <?php
interface Observer
{
public function update(string $event, mixed $data): void;
}
class UserRegisteredEvent
{
private array $observers = [];
public function attach(Observer $observer): void
{
$this->observers[] = $observer;
}
public function detach(Observer $observer): void
{
$this->observers = array_filter(
$this->observers,
fn($o) => $o !== $observer
);
}
public function notify(string $event, mixed $data): void
{
foreach ($this->observers as $observer) {
$observer->update($event, $data);
}
}
public function trigger(array $userData): void
{
// 用户注册逻辑...
echo "用户注册成功\n";
$this->notify('user.registered', $userData);
}
}
class WelcomeEmailObserver implements Observer
{
public function update(string $event, mixed $data): void
{
if ($event === 'user.registered') {
// 发送欢迎邮件
echo "发送欢迎邮件至:{$data['email']}\n";
}
}
}
class PromoCodeObserver implements Observer
{
public function update(string $event, mixed $data): void
{
if ($event === 'user.registered') {
// 发放新人优惠券
echo "发放新人优惠券:WELCOME_{$data['id']}\n";
}
}
}
// 使用
$event = new UserRegisteredEvent();
$event->attach(new WelcomeEmailObserver());
$event->attach(new PromoCodeObserver());
$event->trigger(['id' => 1001, 'email' => 'newuser@example.com', 'name' => '张三']);
?>
6. 依赖注入容器和Service Container
虽然依赖注入本身不是GOF设计模式,但它是现代PHP框架(特别是Laravel)的核心设计理念。简单地说,依赖注入让一个类不再自己创建依赖对象,而是由外部传入。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 <?php
// 不推荐:类自己创建依赖
class UserController_Bad
{
private UserRepository $repository;
public function __construct()
{
$this->repository = new UserRepository(new DatabaseConnection($config));
}
}
// 推荐:依赖注入
class UserController
{
public function __construct(
private UserRepository $repository,
private LoggerInterface $logger,
private MailService $mailer
) {}
public function register(array $data): Response
{
$this->logger->info('User registration started');
$user = $this->repository->create($data);
$this->mailer->sendWelcomeEmail($user);
return new Response(201, ['user_id' => $user->id]);
}
}
// 实现一个简易的依赖注入容器
class SimpleContainer
{
private array $bindings = [];
private array $instances = [];
public function set(string $abstract, Closure $concrete): void
{
$this->bindings[$abstract] = $concrete;
}
public function get(string $abstract): mixed
{
if (isset($this->instances[$abstract])) {
return $this->instances[$abstract];
}
if (isset($this->bindings[$abstract])) {
$this->instances[$abstract] = $this->bindings[$abstract]($this);
return $this->instances[$abstract];
}
throw new \Exception("No binding for {$abstract}");
}
}
?>
7. 数据映射器模式(Data Mapper)
数据映射器模式负责在领域对象和数据库之间传输数据,同时保持两者的独立性。这是Laravel Eloquent ORM底层的重要设计理念,也是Repository模式的核心理念。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 <?php
class User
{
public function __construct(
public ?int $id = null,
public string $name = '',
public string $email = '',
public ?\DateTime $createdAt = null
) {}
}
interface UserMapperInterface
{
public function findById(int $id): ?User;
public function save(User $user): bool;
public function delete(User $user): bool;
}
class PdoUserMapper implements UserMapperInterface
{
private PDO $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
public function findById(int $id): ?User
{
$stmt = $this->pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute([':id' => $id]);
$row = $stmt->fetch();
if (!$row) {
return null;
}
return new User(
id: (int)$row['id'],
name: $row['name'],
email: $row['email'],
createdAt: new \DateTime($row['created_at'])
);
}
public function save(User $user): bool
{
if ($user->id === null) {
// INSERT
$stmt = $this->pdo->prepare(
"INSERT INTO users (name, email) VALUES (:name, :email)"
);
$result = $stmt->execute([
':name' => $user->name,
':email' => $user->email,
]);
if ($result) {
$user->id = (int)$this->pdo->lastInsertId();
}
return $result;
}
// UPDATE
$stmt = $this->pdo->prepare(
"UPDATE users SET name = :name, email = :email WHERE id = :id"
);
return $stmt->execute([
':name' => $user->name,
':email' => $user->email,
':id' => $user->id,
]);
}
public function delete(User $user): bool
{
$stmt = $this->pdo->prepare("DELETE FROM users WHERE id = :id");
return $stmt->execute([':id' => $user->id]);
}
}
?>
在实际项目中如何选择设计模式
很多初学者面对设计模式时最大的困惑是:”我知道这些模式,但不知道什么时候该用。”这里提供一个简单实用的判断方法:
| 你在编码中遇到的问题 | 推荐使用的设计模式 |
|---|---|
| 需要确保全局只有一个实例 | 单例模式 |
| 根据条件创建不同类型的对象 | 工厂方法模式 / 抽象工厂模式 |
| 需要让不兼容的接口协同工作 | 适配器模式 |
| 有一组可互换的算法需要灵活切换 | 策略模式 |
| 一个对象的状态变化需要通知多个其他对象 | 观察者模式 |
| 需要在不修改现有类的情况下扩展功能 | 装饰器模式 |
| 构建复杂对象需要分步骤进行 | 建造者模式 |
实战案例:用设计模式重构一个订单系统
让我们把学到的模式综合应用到一个真实的订单处理系统中。假设我们要开发一个电商平台的订单处理器,需要支持:多种支付方式、多种物流策略、下单后的通知服务。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99 <?php
// 1. 单例 - 配置管理
class ConfigManager
{
private static ?ConfigManager $instance = null;
private array $config;
private function __construct()
{
$this->config = [
'payment' => ['alipay' => true, 'wechat' => true],
'shipping' => ['sf' => ['base' => 12], 'yunda' => ['rate' => 3.5]],
];
}
public static function getInstance(): self
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function get(string $key): mixed
{
return $this->config[$key] ?? null;
}
}
// 2. 工厂方法 - 支付网关创建
interface PaymentHandler
{
public function pay(Order $order): bool;
}
class AlipayHandler implements PaymentHandler { /* ... */ }
class WechatHandler implements PaymentHandler { /* ... */ }
// 3. 策略模式 - 运费计算
interface ShippingCalculator
{
public function calculate(Order $order): float;
}
class SFCalculator implements ShippingCalculator
{
public function calculate(Order $order): float
{
return $order->weight <= 1 ? 12 : 12 + ceil($order->weight - 1) * 5;
}
}
// 4. 观察者模式 - 订单事件通知
interface OrderObserver
{
public function onOrderCreated(Order $order): void;
}
class SmsNotifier implements OrderObserver { /* ... */ }
class EmailNotifier implements OrderObserver { /* ... */ }
class InventoryUpdater implements OrderObserver { /* ... */ }
// 5. 组装在一起
class OrderProcessor
{
private array $observers = [];
public function addObserver(OrderObserver $observer): void
{
$this->observers[] = $observer;
}
public function process(array $orderData): Order
{
$order = new Order($orderData);
// 使用策略计算运费
$shipping = match ($orderData['shipping_method']) {
'sf' => new SFCalculator(),
'yunda' => new YundaCalculator(),
};
$order->shippingCost = $shipping->calculate($order);
// 使用工厂创建支付处理器
$handler = match ($orderData['payment_method']) {
'alipay' => new AlipayHandler(),
'wechat' => new WechatHandler(),
};
$order->paid = $handler->pay($order);
// 通知所有观察者
foreach ($this->observers as $observer) {
$observer->onOrderCreated($order);
}
return $order;
}
}
?>
设计模式在主流PHP框架中的应用
理解设计模式后,你会发现主流PHP框架的核心机制变得豁然开朗:
- Laravel:大量使用了门面模式(Facade)、装饰器模式(中间件管道)、策略模式(各种Manager类)、观察者模式(事件系统)、工厂方法模式(Eloquent ORM的查询构造器)
- Symfony:依赖注入容器是其核心,大量使用适配器模式(各种Bridge组件)、事件分发器(观察者模式)、编译器的装饰器模式
- ThinkPHP:使用了单例模式(容器)、门面模式、依赖注入
- Yii2:依赖注入容器、事件系统(观察者模式)、行为系统(装饰器模式)
总结
设计模式是PHP进阶路上不可或缺的知识储备。本文介绍的7种模式是实际项目中最常用的,掌握了它们就能应对绝大多数编码场景。
最后给出几点实用建议:
- 不要为了用模式而用模式——先考虑简单方案,确认有扩展需求后再引入模式
- 理解框架源码很重要——阅读Laravel或Symfony的源码,看它们如何在真实场景中运用设计模式
- 结合SOLID原则一起学习——设计模式和SOLID原则相辅相成,理解了开闭原则和依赖倒置原则,很多模式的选用就自然明了了
- 多写多练——在自己的项目中尝试用设计模式重构遗留代码,从实践中获得真实感悟
希望这篇文章能帮助你在PHP设计模式的学习和实践中获得真正的提升。如果你有关于某个模式更深入的问题,欢迎在评论区留言讨论。
汤不热吧