在应用程序设计过程中,一些具体操作需要能够支持撤销(undo),例如最近在写的一个文件管理系统。文件的一些基本操作如:rename、copy、cut等,需要支持undo、redo操作来提供更好的用户体验。总所周知,undo、redo操作需要两个模式支撑:备忘录模式(memento)保存对象操作数据状态、命令模式(command)封装用户请求。结合起来可以提供良好的撤销、重做操作。命令模式可以参考上面一篇文章点击打开链接.下面主要说说备忘录模式的实现,如有错误,不令赐教。
备忘录模式主要有3个参与者:
a.保存状态信息的备忘录对象(Memento)
b.产生状态信息的源发器(Originator)
c.备忘录对象的管理器(CareTaker)
源发器提供两个对外的接口:
create_memento(); //保存对象状态信息创建一个备忘录返回 set_memento(Memento $mem); //根据传入的备忘录获取状态信息,恢复状态
备忘录对象提供两个接口:
set_state(State $state); //设备备忘录当前状态 get_state(); //获取备忘录当前状态
备忘录对象实现:
class Memento { private $state; public function get_state(){ return $this->state; } public function set_state(State $state){ $this->state = clone $state; } }
set_state接口通过State参数提示保证参数类型的唯一性。需要注意的是,PHP默认对象赋值的时候不会像C++那般执行拷贝构造函数,PHP基于引用对象计数器,对象赋值默认计数器加一。这里我们使用PHP提供的clone操作保证得到的是一个全新的对象。
源发器实现:
class Originator{ private $state; function _construct(){ $this->state = new State(); $this->state->set('action', 'create originator'); } function do_action1(){ $this->state->set('action', 'do_action 1'); } function do_action2(){ $this->state->set('action', 'do_action 2'); } function create_memento(){ $mem = new Memento(); $men->set_state($this->state); return $mem; } function set_memento(Memento $mem){ $this->state = $mem->get_state(); } function dump(){ echo $this->state->get('action') . "\n"; } }
状态信息对象:
class State{ private $values = array(); public function set($key, $value){ $this->values[$key] = $value; } public function get($key){ if(isset($this->values[$key])){ return $this->values[$key]; } return null; } }
最后是备忘录管理器的简单实现:
class CareTaker{ private $command; function __construct($cmd="Originator1"){ $this->command = $cmd; } private function do_execute(){ switch($this->command){ case 'Originator1':{ $action = new Originator(); $mem1 = $action->create_memento(); $action->dump(); $action->do_action1(); $mem2 = $action->create_memento(); $action->dump(); $action->do_action2(); $mem3 = $action->create_memento(); $action->dump(); //状态恢复 $action->set_memento($mem2); $action->dump(); $action->set_memento($mem1); $action->dump(); } } } }
这里有几个地方可以改进的,首先,管理器应该提供一个队列来管理一系列的备忘录对象。其次客户端请求命令用一个大大的switch-case做分发而不是利用命令模式把请求封装为对象,导致了类结构混乱。下一篇将实现一个完整版本的undo-redo操作。
The end.
时间: 2024-10-09 03:18:00