设计模式解析:命令模式的实践艺术
模式定义
命令模式(Command Pattern)是行为型设计模式,将请求封装为独立对象,实现请求的参数化传递、队列管理和撤销操作。该模式通过解耦请求发起者与执行者,构建灵活可扩展的操作系统。
核心思想
请求对象化:将操作封装为独立命令对象
解耦调用者与执行者:调用方无需了解具体执行细节
支持事务操作:实现命令序列的原子执行
扩展性强:轻松添加新命令类型
适用场景
需要实现操作撤销/重做功能
构建任务队列系统
支持事务性操作
需要参数化请求(如GUI按钮事件)
模式结构
Command:命令接口
ConcreteCommand:具体命令实现
Invoker:命令触发者
Receiver:命令执行者
Client:创建并配置命令对象
PHP实现示例:智能家居控制系统
<?php
// 命令接口
interface SmartCommand {
public function execute(): void;
public function undo(): void;
}
// 接收者:灯具
class Light {
private $brightness = 0;
public function on(int $level = 50): void {
$this->brightness = $level;
echo "灯具开启,亮度:{$this->brightness}%\n";
}
public function off(): void {
$this->brightness = 0;
echo "灯具关闭\n";
}
public function getBrightness(): int {
return $this->brightness;
}
}
// 具体命令:开灯
class LightOnCommand implements SmartCommand {
private $light;
private $prevLevel;
public function __construct(Light $light) {
$this->light = $light;
}
public function execute(): void {
$this->prevLevel = $this->light->getBrightness();
$this->light->on(75);
}
public function undo(): void {
if ($this->prevLevel > 0) {
$this->light->on($this->prevLevel);
} else {
$this->light->off();
}
}
}
// 接收者:空调
class AirConditioner {
private $temperature = 26;
public function setTemperature(int $temp): void {
$this->temperature = $temp;
echo "空调设置:{$temp}℃\n";
}
public function getTemperature(): int {
return $this->temperature;
}
}
// 具体命令:调温
class TemperatureCommand implements SmartCommand {
private $ac;
private $targetTemp;
private $previousTemp;
public function __construct(AirConditioner $ac, int $temp) {
$this->ac = $ac;
$this->targetTemp = $temp;
}
public function execute(): void {
$this->previousTemp = $this->ac->getTemperature();
$this->ac->setTemperature($this->targetTemp);
}
public function undo(): void {
$this->ac->setTemperature($this->previousTemp);
}
}
// 调用者:智能遥控器
class SmartRemote {
private $commands = [];
private $undoStack = [];
public function setCommand(SmartCommand $cmd): void {
$this->commands[] = $cmd;
}
public function executeAll(): void {
foreach ($this->commands as $cmd) {
$cmd->execute();
$this->undoStack[] = $cmd;
}
$this->commands = [];
}
public function undoLast(): void {
if (!empty($this->undoStack)) {
$cmd = array_pop($this->undoStack);
$cmd->undo();
}
}
}
// 客户端使用
$remote = new SmartRemote();
$livingRoomLight = new Light();
$bedroomAC = new AirConditioner();
// 配置命令序列
$remote->setCommand(new LightOnCommand($livingRoomLight));
$remote->setCommand(new TemperatureCommand($bedroomAC, 22));
// 执行批处理命令
echo "执行命令序列:\n";
$remote->executeAll();
// 撤销最后一个命令
echo "\n撤销最后操作:\n";
$remote->undoLast();
/* 输出:
执行命令序列:
灯具开启,亮度:75%
空调设置:22℃
撤销最后操作:
空调设置:26℃
*/
Go实现示例:事务型任务队列
package main
import (
"fmt"
"sync"
)
// 命令接口
type Command interface {
Execute()
Undo()
}
// 接收者:邮件服务
type EmailService struct{}
func (e *EmailService) Send(to, content string) {
fmt.Printf("发送邮件至 %s: %s\n", to, content)
}
func (e *EmailService) Recall(id string) {
fmt.Printf("撤回邮件 %s\n", id)
}
// 具体命令:发送邮件
type SendEmailCommand struct {
service *EmailService
to string
content string
messageID string
mu sync.Mutex
}
func NewEmailCommand(svc *EmailService, to, content string) *SendEmailCommand {
return &SendEmailCommand{
service: svc,
to: to,
content: content,
}
}
func (c *SendEmailCommand) Execute() {
c.mu.Lock()
defer c.mu.Unlock()
c.service.Send(c.to, c.content)
c.messageID = fmt.Sprintf("MSG-%d", time.Now().UnixNano())
}
func (c *SendEmailCommand) Undo() {
c.mu.Lock()
defer c.mu.Unlock()
if c.messageID != "" {
c.service.Recall(c.messageID)
c.messageID = ""
}
}
// 接收者:日志服务
type Logger struct{}
func (l *Logger) Record(action string) {
fmt.Printf("[日志] 操作记录: %s\n", action)
}
// 具体命令:日志记录
type LogCommand struct {
logger *Logger
action string
}
func (c *LogCommand) Execute() {
c.logger.Record(c.action)
}
func (c *LogCommand) Undo() {
c.logger.Recall(c.action)
}
// 事务处理器
type TransactionProcessor struct {
commands []Command
wg sync.WaitGroup
}
func (tp *TransactionProcessor) AddCommand(cmd Command) {
tp.commands = append(tp.commands, cmd)
}
func (tp *TransactionProcessor) ExecuteConcurrent() {
tp.wg.Add(len(tp.commands))
for _, cmd := range tp.commands {
go func(c Command) {
defer tp.wg.Done()
c.Execute()
}(cmd)
}
tp.wg.Wait()
}
func main() {
emailSvc := &EmailService{}
logger := &Logger{}
processor := &TransactionProcessor{}
processor.AddCommand(NewEmailCommand(emailSvc, "client@example.com", "合同签署通知"))
processor.AddCommand(&LogCommand{logger, "发送合同邮件"})
fmt.Println("并发执行命令:")
processor.ExecuteConcurrent()
fmt.Println("\n撤销操作:")
for _, cmd := range processor.commands {
cmd.Undo()
}
}
/* 输出示例:
并发执行命令:
发送邮件至 client@example.com: 合同签署通知
[日志] 操作记录: 发送合同邮件
撤销操作:
撤回邮件 MSG-162947123456789
[日志] 撤回操作: 发送合同邮件
*/
模式优缺点
✅ 优点:
解耦请求发起者与执行者
支持事务操作与撤销机制
方便扩展新命令类型
支持命令组合与队列管理
❌ 缺点:
增加系统复杂度
可能产生大量命令类
维护撤销日志消耗资源
不同语言实现差异
模式演进方向
复合命令
实现宏命令(命令组合)持久化命令
将命令序列存储到数据库异步队列
结合消息队列实现分布式命令命令版本控制
支持操作历史追溯智能重试
为命令添加自动重试机制
命令模式VS其他模式
最佳实践指南
命令粒度控制
避免过于细碎的命令对象生命周期管理
及时清理已完成命令权限控制
为敏感命令添加执行权限校验日志记录
记录命令执行历史性能优化
对高频命令使用对象池
总结
命令模式通过将操作抽象为独立对象,构建了灵活可控的请求管理系统。该模式在以下场景表现卓越:
需要实现操作撤销/重做功能
构建任务队列或批处理系统
实现GUI操作/快捷键功能
需要异步执行或延迟处理请求
PHP与Go的实现差异体现了不同语言的特性优势:
PHP通过类继承体系实现直观的命令管理
Go利用协程和接口实现高性能并发处理
实际应用中需注意:
合理设计命令对象的粒度
控制命令历史记录的内存占用
保证命令的幂等性和事务性
在灵活性与复杂度之间找到平衡点
掌握命令模式的关键在于理解"操作对象化"的设计哲学,这种将行为抽象为独立实体的思想,是构建可扩展、可维护系统的核心方法。当需要实现复杂的操作管理时,命令模式能提供优雅的解决方案。