首页 > 代码库 > 浅析备忘录模式

浅析备忘录模式

      在应用程序设计过程中,一些具体操作需要能够支持撤销(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.

    

浅析备忘录模式