重做(redo)和撤銷(undo)的完整實現

來源:互聯網
上載者:User
undo-redo需要備忘錄模式和命令模式做支撐,之前有學習過了command模式和memento模式的一些基本知識。這裡要結合兩個模式實現一個undo-redo操作的模組,鞏固所學的知識。

系統框圖:

命令分發控制器主要有四個任務:
1.系統初始化,載入系統配置參數並把這些資料緩衝起來,這些應用程式層級的配置參數可以使用序列化機制,把資料緩衝而不用每次去讀取檔案,加快訪問效率。
2.根據前端請求,收集參數產生一個請求(Request)。
3.把請求映射到具體商務邏輯的命令模組(Command)。
4.執行操作並把結果返回給前端視圖。

商務邏輯層根據傳入的context對象可以擷取執行參數,執行完畢後還可以把執行結果通過context對象返回給上一層。

命令分發控制器的實現:

class Controller{private function __construct() {}static function run(){$instance = new Controller();$instance->init();$instance->handleRequest();}function init(){$application= \base\ApplicationHelper::instance();$application->system_init();}function handleRequest(){$request  = new \controller\Request();$cmd_r = new \command\CommandResolver();$cmd = $cmd_r->get_command($request);$cmd->execute($request);}}
通過把建構函式聲明為private,controller為一個單例。

對於類似PHP這樣的解釋型的語言,要實現undeo/redo機制,必須用到一些緩衝機制(session)來儲存命令執行的記錄。這裡的session模組主要負責維護一個命令記錄,其實現如下:

namespace base;require_once('session_registry.php');class SessionMementoTaker extends SessionRegistry{const COMMAND_COUNT = 5;private $persent = 0;private $cmd_stack = array();static public function instance(){return parent::instance();}public function push_command(Command $cmd){$this->cmd_stack = self::instance()->get('cmd_stack');if(!empty($this->cmd_stack)){if(count($this->cmd_stack) >self::COMMAND_COUNT){array_shift($this->cmd_stack);reset($this->cmd_stack);}} array_push($this->cmd_stack, $cmd);$this->persent = count($this->cmd_stack) + 1;self::instance()->set('cmd_stack', $this->cmd_stack);self::instance()->set('cmd_persent', $this->persent);}public function get_undo_command(){$this->persent = self::instance()->get('cmd_persent');$this->cmd_stack = self::instance()->get('cmd_stack');if(!empty($this->cmd_stack) && $this->persent > 0){$command = $this->cmd_stack[--$this->persent];self::instance()->set('cmd_persent', $this->persent);return $command;}return null;}public function get_redo_command(){$this->persent = self::instance()->get('cmd_persent');$this->cmd_stack = self::instance()->get('cmd_stack');if(!empty($this->cmd_stack) && $this->persent < count($this->cmd_stack)){$command = $this->cmd_stack[$this->persent++];self::instance()->set('cmd_persent', $this->persent);return $command;}return null;}}

SessionMementoTaker的實現基於之前實現的一個會話(URL 註冊機制)。根據cookies裡面儲存的會話ID恢複不同的對象資料,可以達到同一使用者多次請求訪問同一對象資料的目的。SessionMementoTaker額外提供了三個介面,push_command操作添加命令到曆史命令列表。曆史命令列表最大長度為5個,超過5個把最開始的命令移除。另外,push_command相當於添加一個新的命令,要把命令指標(persent)移動到最新的位置。捨棄之前的狀態。get_undo_command擷取最後一次執行的曆史命令並更新指標,get_redo_command同理。
曆史命令列表: command1---command2---command3---* 星號表示persent,指向最新要執行的命令。
一次undo操作:command1---command2-*--command3--- 復原之後persent指標往後移動。
一次undo操作:command1--*command2----command3--- 復原之後persent指標往後移動。
一次redo操作:command1---command2-*--command3--- 重做之後persent指標往前移動。
push_command:command1---command2---command3---command4---* persent更新到最前端
在這裡把單個command對象看成是一個原發器(Originator)。根據需要主動建立一個備忘錄(memento)儲存此刻它的內部狀態,並把command對象放入曆史命令記錄列表。

command基類實現:

namespace woo\command;require_once('../memento/state.php');require_once('../memento/memento.php');abstract class Command {protected $state;final function __construct(){$this->state = new \woo\memento\State();}function execute(\woo\controller\Request $request) {$this->state->set('request', $request);$this->do_execute($request);}abstract function do_execute(\woo\controller\Request $request);function do_unexecute(\woo\controller\Request $request) {}public function get_state(){return $this->state;}public function set_state(State $state){$this->state = $state;}public function get_request(){if(isset($this->state)){return $this->state->get('request');}return null;}public function set_request(\woo\controller\Request $request){if(isset($this->state)){return $this->state->set('request', $request);}}public function create_memento(){\woo\base\SessionMementoTaker::push_command($this);$mem = new \woo\memento\Memento();$mem->set_state($this->state);        return $mem;}public function set_memento(Memento $mem){$this->state = $mem->get_state();}}

命令任務在執行開始的時候儲存請求命令的參數,在命令執行過程中還可以儲存其他必要的參數。由於有些命令不支援撤銷操作所以在父類實現裡一個空的unexecute;

儲存命令狀態的對象:

class State{private $values = array();function __construct(){}public function set($key, $value){$this->values[$key] = $value;}public function get($key){if(isset($this->values[$key])){return $this->values[$key];}return null;}}

一個支援undo-redo的複製檔案的命令:

namespace woo\command;require_once('request.php');require_once('command.php');require_once('../base/registry.php');require_once('../file_manager.php');require_once('../base/session_memento.php');class CopyCommand extends Command {function do_execute(\controller\Request $request) {$src_path = $request->get_property('src');$dst_path = $request->get_property('dst');$this->state->set('src_path', $src_path);$this->state->set('dst_path', $dst_path);$this->create_memento();$file_manager = \base\Registry::file_manager();$ret = $file_manager->copy($src_path, $dst_path);$request->add_feedback($ret);//...}}
命令對象要做的工作比較單一:擷取參數(校正參數),儲存必要的狀態資訊,把控制權交給具體的商務邏輯對象。添加執行結果並返回。不同的命令需要不同的請求參數,一些命令根本不需要也不支援撤銷操作,所以可以選擇性的執行create_memento操作。

最後是要實現的undo-redo,在這裡我把undo/redo也看成是一次普通的命令請求,而不需要在控制器做額外的分發處理。


撤銷命令:

namespace woo\command;require_once('request.php');require_once('command.php');require_once('../base/registry.php');require_once('../base/session_memento.php');class UndoCommand extends Command{public function do_execute(\controller\Request $request){$command = \base\SessionMementoTaker::get_undo_command();if(isset($command)){$old_req = $command->get_request();$command->do_unexecute($old_req);$request->set_feedback($old_req->get_feedback());} else{$request->add_feedback('undo command not fount');}return;}}

重做命令:

namespace woo\command;require_once('request.php');require_once('command.php');require_once('../base/registry.php');class RedoCommand extends Command {public function do_execute(\woo\controller\Request $request){$command = \woo\base\SessionMementoTaker::get_redo_command();if(isset($command)){$old_req = $command->get_request();$command->do_execute($old_req);$request->set_feedback($old_req->get_feedback());} else{$request->add_feedback('undo command not fount');}return;}}

The end.

以上就介紹了重做(redo)和撤銷(undo)的完整實現,包括了方面的內容,希望對PHP教程有興趣的朋友有所協助。

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.