本文目录导读:

我来为您介绍如何在PHP项目中实现备忘录(Memento)模式和撤销功能。
备忘录模式实现
基础备忘录类
<?php
/**
* 备忘录类 - 存储状态快照
*/
class Memento
{
private $state;
private $timestamp;
public function __construct($state)
{
$this->state = $state;
$this->timestamp = time();
}
public function getState()
{
return $this->state;
}
public function getTimestamp()
{
return $this->timestamp;
}
public function getName()
{
return date('Y-m-d H:i:s', $this->timestamp);
}
}
/**
* 发起人 - 创建和恢复备忘录
*/
class Originator
{
private $state;
public function setState($state)
{
echo "设置状态为: " . $state . "\n";
$this->state = $state;
}
public function getState()
{
return $this->state;
}
// 保存状态到备忘录
public function saveStateToMemento()
{
echo "保存状态: " . $this->state . "\n";
return new Memento($this->state);
}
// 从备忘录恢复状态
public function getStateFromMemento(Memento $memento)
{
$this->state = $memento->getState();
echo "恢复到状态: " . $this->state . "\n";
}
}
/**
* 管理者 - 管理备忘录历史记录
*/
class Caretaker
{
private $mementoList = [];
private $currentIndex = -1;
private $maxHistory = 50; // 最大历史记录数
// 添加备忘录
public function add(Memento $memento)
{
// 如果在撤销后添加新操作,删除当前索引后的所有记录
if ($this->currentIndex < count($this->mementoList) - 1) {
array_splice($this->mementoList, $this->currentIndex + 1);
}
$this->mementoList[] = $memento;
// 限制历史记录数量
if (count($this->mementoList) > $this->maxHistory) {
array_shift($this->mementoList);
}
$this->currentIndex = count($this->mementoList) - 1;
}
// 获取上一个状态(撤销)
public function undo()
{
if ($this->currentIndex > 0) {
$this->currentIndex--;
return $this->mementoList[$this->currentIndex];
}
return null;
}
// 获取下一个状态(重做)
public function redo()
{
if ($this->currentIndex < count($this->mementoList) - 1) {
$this->currentIndex++;
return $this->mementoList[$this->currentIndex];
}
return null;
}
// 获取当前状态
public function getCurrent()
{
if ($this->currentIndex >= 0 && $this->currentIndex < count($this->mementoList)) {
return $this->mementoList[$this->currentIndex];
}
return null;
}
// 获取历史记录数量
public function getHistoryCount()
{
return count($this->mementoList);
}
// 获取当前索引
public function getCurrentIndex()
{
return $this->currentIndex;
}
// 是否可以撤销
public function canUndo()
{
return $this->currentIndex > 0;
}
// 是否可以重做
public function canRedo()
{
return $this->currentIndex < count($this->mementoList) - 1;
}
// 清空历史
public function clear()
{
$this->mementoList = [];
$this->currentIndex = -1;
}
}
实际应用示例 - 文本编辑器
<?php
/**
* 文本编辑器 - 使用备忘录模式实现撤销/重做
*/
class TextEditor
{
private $content = '';
private $caretaker;
private $originator;
public function __construct()
{
$this->caretaker = new Caretaker();
$this->originator = new Originator();
}
// 写入文本
public function write($text)
{
// 保存当前状态
$this->saveState();
$this->content .= $text;
echo "写入: '" . $text . "'\n";
}
// 删除文本
public function delete($length = null)
{
$this->saveState();
if ($length === null) {
$deleted = substr($this->content, -1);
$this->content = substr($this->content, 0, -1);
echo "删除: '" . $deleted . "'\n";
} else {
$deleted = substr($this->content, -$length);
$this->content = substr($this->content, 0, -$length);
echo "删除: '" . $deleted . "'\n";
}
}
// 替换文本
public function replace($search, $replace)
{
$this->saveState();
$this->content = str_replace($search, $replace, $this->content);
echo "替换: '" . $search . "' -> '" . $replace . "'\n";
}
// 清空文本
public function clear()
{
$this->saveState();
$this->content = '';
echo "清空文本\n";
}
// 保存当前状态
private function saveState()
{
$this->originator->setState($this->content);
$this->caretaker->add($this->originator->saveStateToMemento());
}
// 撤销
public function undo()
{
$memento = $this->caretaker->undo();
if ($memento) {
$this->content = $memento->getState();
echo "撤销操作\n";
} else {
echo "无法撤销\n";
}
}
// 重做
public function redo()
{
$memento = $this->caretaker->redo();
if ($memento) {
$this->content = $memento->getState();
echo "重做操作\n";
} else {
echo "无法重做\n";
}
}
// 获取当前内容
public function getContent()
{
return $this->content;
}
// 显示状态信息
public function getStatus()
{
return [
'content' => $this->content,
'canUndo' => $this->caretaker->canUndo(),
'canRedo' => $this->caretaker->canRedo(),
'historyCount' => $this->caretaker->getHistoryCount()
];
}
}
// 使用示例
$editor = new TextEditor();
echo "=== 文本编辑器演示 ===\n\n";
$editor->write("Hello");
echo "内容: '" . $editor->getContent() . "'\n\n";
$editor->write(" World");
echo "内容: '" . $editor->getContent() . "'\n\n";
$editor->write("!");
echo "内容: '" . $editor->getContent() . "'\n\n";
// 撤销两次
$editor->undo();
echo "内容: '" . $editor->getContent() . "'\n\n";
$editor->undo();
echo "内容: '" . $editor->getContent() . "'\n\n";
// 重做一次
$editor->redo();
echo "内容: '" . $editor->getContent() . "'\n\n";
数据库操作撤销
<?php
/**
* 数据库操作撤销管理器
*/
class DatabaseOperationManager
{
private $history = [];
private $currentIndex = -1;
private $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
// 执行数据库操作
public function execute($sql, $params = [], $rollbackSql = null)
{
// 开始事务
$this->pdo->beginTransaction();
try {
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
// 保存操作记录
$operation = [
'sql' => $sql,
'params' => $params,
'rollbackSql' => $rollbackSql,
'timestamp' => time()
];
// 如果在撤销后添加新操作,删除当前索引后的记录
if ($this->currentIndex < count($this->history) - 1) {
array_splice($this->history, $this->currentIndex + 1);
}
$this->history[] = $operation;
$this->currentIndex = count($this->history) - 1;
$this->pdo->commit();
return true;
} catch (Exception $e) {
$this->pdo->rollBack();
throw $e;
}
}
// 撤销操作
public function undo()
{
if ($this->currentIndex < 0) {
return false;
}
$operation = $this->history[$this->currentIndex];
if ($operation['rollbackSql']) {
$this->pdo->beginTransaction();
try {
$stmt = $this->pdo->prepare($operation['rollbackSql']);
$stmt->execute();
$this->pdo->commit();
$this->currentIndex--;
return true;
} catch (Exception $e) {
$this->pdo->rollBack();
throw $e;
}
}
return false;
}
// 重做操作
public function redo()
{
$nextIndex = $this->currentIndex + 1;
if ($nextIndex >= count($this->history)) {
return false;
}
$operation = $this->history[$nextIndex];
$this->pdo->beginTransaction();
try {
$stmt = $this->pdo->prepare($operation['sql']);
$stmt->execute($operation['params']);
$this->pdo->commit();
$this->currentIndex = $nextIndex;
return true;
} catch (Exception $e) {
$this->pdo->rollBack();
throw $e;
}
}
// 获取操作历史
public function getHistory()
{
$history = [];
for ($i = 0; $i <= $this->currentIndex; $i++) {
$op = $this->history[$i];
$history[] = [
'index' => $i,
'time' => date('Y-m-d H:i:s', $op['timestamp']),
'sql' => $op['sql']
];
}
return $history;
}
}
完整的UndoManager类
<?php
/**
* 通用撤销管理器
*/
class UndoManager
{
private static $instance;
private $stacks = [];
private function __construct() {}
public static function getInstance()
{
if (!self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
// 为特定操作创建堆栈
public function createStack($name)
{
if (!isset($this->stacks[$name])) {
$this->stacks[$name] = [
'undo' => [],
'redo' => [],
'maxSize' => 50
];
}
}
// 执行操作并保存撤销信息
public function execute($stackName, $action, $undoData)
{
if (!isset($this->stacks[$stackName])) {
$this->createStack($stackName);
}
$stack = &$this->stacks[$stackName];
// 清空重做堆栈(新操作后不能重做)
$stack['redo'] = [];
// 添加到撤销堆栈
$stack['undo'][] = [
'action' => $action,
'data' => $undoData,
'timestamp' => time()
];
// 限制堆栈大小
if (count($stack['undo']) > $stack['maxSize']) {
array_shift($stack['undo']);
}
}
// 撤销
public function undo($stackName)
{
if (!isset($this->stacks[$stackName]) || empty($this->stacks[$stackName]['undo'])) {
return null;
}
$stack = &$this->stacks[$stackName];
$operation = array_pop($stack['undo']);
// 添加到重做堆栈
$stack['redo'][] = $operation;
return $operation;
}
// 重做
public function redo($stackName)
{
if (!isset($this->stacks[$stackName]) || empty($this->stacks[$stackName]['redo'])) {
return null;
}
$stack = &$this->stacks[$stackName];
$operation = array_pop($stack['redo']);
// 添加到撤销堆栈
$stack['undo'][] = $operation;
return $operation;
}
// 检查是否可以撤销
public function canUndo($stackName)
{
return isset($this->stacks[$stackName]) && !empty($this->stacks[$stackName]['undo']);
}
// 检查是否可以重做
public function canRedo($stackName)
{
return isset($this->stacks[$stackName]) && !empty($this->stacks[$stackName]['redo']);
}
// 清空堆栈
public function clearStack($stackName)
{
if (isset($this->stacks[$stackName])) {
$this->stacks[$stackName]['undo'] = [];
$this->stacks[$stackName]['redo'] = [];
}
}
// 获取堆栈状态
public function getStackStatus($stackName)
{
if (!isset($this->stacks[$stackName])) {
return null;
}
return [
'undoCount' => count($this->stacks[$stackName]['undo']),
'redoCount' => count($this->stacks[$stackName]['redo']),
'canUndo' => $this->canUndo($stackName),
'canRedo' => $this->canRedo($stackName)
];
}
}
实际应用示例
<?php
// 示例1:文档编辑器
class Document
{
private $content = '';
private $undoManager;
public function __construct()
{
$this->undoManager = UndoManager::getInstance();
$this->undoManager->createStack('document');
}
public function insert($text)
{
$oldContent = $this->content;
$this->content .= $text;
$this->undoManager->execute('document', 'insert', [
'oldContent' => $oldContent,
'newContent' => $this->content
]);
echo "插入: '$text'\n";
}
public function undo()
{
$operation = $this->undoManager->undo('document');
if ($operation) {
$data = $operation['data'];
$this->content = $data['oldContent'];
echo "撤销插入\n";
}
}
public function redo()
{
$operation = $this->undoManager->redo('document');
if ($operation) {
$data = $operation['data'];
$this->content = $data['newContent'];
echo "重做插入\n";
}
}
public function getContent()
{
return $this->content;
}
}
// 使用示例
$doc = new Document();
$doc->insert("Hello");
$doc->insert(" World");
$doc->insert("!");
echo "内容: " . $doc->getContent() . "\n";
$doc->undo();
echo "撤销后: " . $doc->getContent() . "\n";
$doc->undo();
echo "再次撤销: " . $doc->getContent() . "\n";
$doc->redo();
echo "重做后: " . $doc->getContent() . "\n";
最佳实践建议
性能优化
<?php
/**
* 带压缩的备忘录
*/
class CompressedMemento
{
private $compressedState;
private $timestamp;
public function __construct($state)
{
// 压缩状态以节省内存
$this->compressedState = gzcompress(serialize($state));
$this->timestamp = time();
}
public function getState()
{
return unserialize(gzuncompress($this->compressedState));
}
}
/**
* 差异备忘录 - 只存储变化部分
*/
class DiffMemento
{
private $diff;
private $timestamp;
public function __construct($oldState, $newState)
{
// 计算差异
$this->diff = $this->computeDiff($oldState, $newState);
$this->timestamp = time();
}
private function computeDiff($old, $new)
{
// 简单差异计算
if (is_string($old) && is_string($new)) {
$minLen = min(strlen($old), strlen($new));
$diffStart = 0;
for ($i = 0; $i < $minLen; $i++) {
if ($old[$i] !== $new[$i]) {
$diffStart = $i;
break;
}
}
return [
'start' => $diffStart,
'deleted' => substr($old, $diffStart),
'inserted' => substr($new, $diffStart)
];
}
return ['old' => $old, 'new' => $new];
}
}
清理策略
<?php
/**
* 自动清理的备忘录管理器
*/
class AutoCleanCaretaker
{
private $mementos = [];
private $maxSize = 100;
private $timeToLive = 3600; // 1小时
public function add(Memento $memento)
{
$this->cleanup();
$this->mementos[] = $memento;
if (count($this->mementos) > $this->maxSize) {
array_shift($this->mementos);
}
}
private function cleanup()
{
$now = time();
$this->mementos = array_filter($this->mementos, function($memento) use ($now) {
return ($now - $memento->getTimestamp()) < $this->timeToLive;
});
}
}
这个实现提供了:
- 基础的备忘录模式 - 保存和恢复对象状态
- 完整的撤销/重做功能 - 支持多级撤销和重做
- 历史记录管理 - 限制历史记录数量,防止内存溢出
- 实际应用示例 - 文本编辑器、数据库操作等
- 性能优化 - 压缩、差异存储等
- 线程安全 - 使用单例模式
使用注意事项:
- 合理设置历史记录上限
- 考虑内存消耗,特别是大对象的序列化
- 实现清理策略,定期清理过期记录
- 注意对象克隆的深度问题
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播