重做(redo)和撤銷(undo)的完整實現
系統框圖:
命令分發控制器主要有四個任務:
1.系統初始化,載入系統配置引數並把這些資料快取起來,這些應用程式級別的配置引數可以使用序列化機制,把資料快取而不用每次去讀取檔案,加快訪問效率。
2.根據前端請求,收集引數生成一個請求(Request)。
3.把請求對映到具體業務邏輯的命令模組(Command)。
4.執行操作並把結果返回給前端檢視。
業務邏輯層根據傳入的context物件可以獲取執行引數,執行完畢後還可以把執行結果通過context物件返回給上一層。
命令分發控制器的實現:
通過把建構函式宣告為private,controller為一個單例。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); } }
對於類似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的實現基於之前實現的一個會話(登錄檔模式)。根據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)的完整實現
undo-redo需要備忘錄模式和命令模式做支撐,之前有學習過了command模式和memento模式的一些基本知識。這裡要結合兩個模式實現一個undo-redo操作的模組,鞏固所學的知識。 系統框圖: 命令分發控制器主要有四個任務: 1.
數據庫的備份與還原系列——單表備份和恢復詳細完整實現
單表備份 單表還原 表定義備份還原 表數據備份還原 參考實現:https://www.percona.com/doc/percona-xtrabackup/LATEST/innobackupex/innobackupex_script.htmlRestoring Individual Tabl
6. Oracle 回滾(ROLLBACK)和撤銷(UNDO)
切換 ... nbsp 實現 再次 oba orm 分鐘 employees 轉載自:http://blog.csdn.net/leshami/article/details/5731158 一、回滾(ROLLBACK)和撤銷(UNDO) 回滾和前滾是保證Oracle
安卓開發實戰之app之版本更新升級(DownloadManager和http下載)完整實現
前言 本文將講解app的升級與更新。一般而言使用者使用App的時候升級提醒有兩種方式獲得: 一種是通過應用市場 獲取 一種是開啟應用之後提醒使用者更新升級 而更新操作一般是在使用者點選了升級按鈕之後開始執行的,這裡的升級操作也分為兩種形式: 一般升
第十二章:重做日誌和歸檔
關系數據庫 數據庫管理系統 當前 維護 height 進程 dba ren 情況 [大綱]? 重做日誌文件、歸檔日誌文件的結構?重做日誌文件、歸檔日誌文件工作過程? 管理重做日誌文件、歸檔日誌一、在線重做日誌文件1.重做日誌概述 在數據庫的使用過程中,可能會
Android:基於EditText實現撤銷和重做機制
一、 場景描述和思路分析 說到撤銷和重做想必大家腦海中浮現的一定是Ctrl+Z、Ctrl+Y這兩個快捷鍵,平常生產開發的時候也少不了要和這兩個按鍵打交道。作為一個開發者筆者自然對其中的實現方法感到好奇,想必閱讀此文的你也是一樣的。 如果
Command(命令)——物件行為型模式(通過Command設計模式實現WinForm表單維護的撤銷與重做功能)
Command(命令)——物件行為型模式(通過Command設計模式實現WinForm表單維護的撤銷與重做功能) 意圖 動機 典型場景 程式碼實現 ICommand介面,定義execute和undo操作 OperationCom
【程式設計模式】(一) ------ 命令模式 和 “重做” 及 “撤銷”
前言 本文及以後該系列的篇章都是本人對 《遊戲程式設計模式》這本書的閱讀理解,從中對一些原理,用更直白的語言描述出來,並對部分思路或功能進行初步實現。而本文所描述的 命令模式, 相信讀者應該都有了解過或聽說過,如果尚有疑惑的讀者,我希望本文能對你有所幫助。 命令模式是設計模式中的一種,但該系列所指的程式設計模
MySQL中的重做日誌(redo log),回滾日誌(undo log),以及二進位制日誌(binlog)的簡單總結
MySQL中有六種日誌檔案, 分別是:重做日誌(redo log)、回滾日誌(undo log)、二進位制日誌(binlog)、錯誤日誌(errorlog)、慢查詢日誌(slow query log)、一般查詢日誌(general log),中繼日誌(relay log)。 其中重做日誌和回滾日誌與
Android簡單塗鴉以及撤銷、重做的實現方法
前段時間研究了下塗鴉功能的實現,其實單獨的塗鴉實現起來還是挺簡單的,關鍵的技術難點是撤銷與重做功能的實現。但是這裡暫時只說明下塗鴉功能的實現,高手勿噴哈,而且該功能在Android SDK提供的APIDemo當中就有的,但是如果能夠將該地方的知識點搞懂的話,我認為View
CSS3實現五子棋Web小遊戲,Canvas畫布和DOM兩種實現,並且具有悔棋和撤銷悔棋功能。
posit oct padding 角色 sar pac osi fse ech 用Canvas實現五子棋的思路: 1、點擊棋盤,獲取坐標x,y,計算出棋子的二維數組坐標i和j, 2、棋子的實現,先arc一個圓,再填充漸變色。 3、下完一步棋後切換畫筆和角色。 4、贏法算法
簡單記錄一次REDO文件損壞報錯 ORA-00333重做日誌讀取塊出錯
clas 後者 利用 實例恢復 poi cancel true cover html 一.故障描寫敘述 首先是實例恢復須要用到的REDO文件損壞 二、解決方法 1.對於非當前REDO或者當前REDO可是無活動事務使用下面CLEAR命令: 用CLEAR命令重建該日誌
Oracle Logminer 分析重做日誌RedoLog和歸檔日誌ArchiveLog
cti data 格式 保存 命令 重啟 msl dba object 在實際開發過程中,有時我們很有可能需要某個表的操作痕跡,或通過記錄的SQL語句進行有目的性的數據恢復(此時POINT-IN-TIME恢復已經滿足不了更細的粒度)、或僅僅是查看;
修改重做日誌文件(redo log)大小
inactive 訪問 結束 過渡 操作 div check 日誌組 soft 重做日誌相關數據字典 1、v$log 記錄數據庫中有多少個重做日誌組,每個組中有多少個成員、日誌大小及狀態 2、v$logfile 記錄著每個日誌組成員的屬性、文件路徑、文件名、狀態
資料庫日誌redo和undo
資料庫的ACID屬性 Atomicity:原子性,以事物transact為最小單位,事物中的所有操作,要麼都執行完,要麼都不執行,不存在一部分操作執行,另一部分操作不執行的情況。 Consistency:一致性,在事物開始和事物完成後,資料庫的完整性限制不會改變。 Isolation:隔離性,同一個資料
資料庫的REDO和UNDO機制
UNDO和REDO機制 redo undo機制是在資料庫引擎曾實現的。 undo機制 UNDO機制如下: 假設有A、B兩個資料,值分別為1,2。 A.事務開始. B.記錄A=1到undo log. C.修改A=3. D.記錄B=2到undo log. E.修改B=4. F.將un
撤銷和回退的實現
功能點描述 大家應該都使用過瀏覽器的後退和前進功能,它就是我們今天的主題:撤銷和回退。其實不光是在瀏覽器裡面,在眾多的工具軟體內也都有類似的功能,遠的不說,例如:vscode、ppt。 實現思路 在說實現思路之前,我先上一張圖: 眼尖的同學可能已經看到了 Stack ,是的,你想的沒錯,
C# Command命令(行為型模式)+佇列 實現事務,帶非同步命令重試機制和生命週期
一、簡介 耦合是軟體不能抵禦變變化的根本性原因,不僅實體物件與實體物件之間有耦合關係(如建立性設計模式存在的原因),物件和行為之間也存在耦合關係. 二、實戰 1、常規開發中,我們經常會在控制器中或者Main方法中呼叫多個物件,進行批量的操作(完成一次事務性的操作),像下面這樣:
網站重定向 301和302重定向的php實現
內容來源自我的部落格:http://www.jcsoo.com/wordpress/?p=15 前幾天搭建好了wordpress的部落格,但是我發現了一個問題,就是我的部落格在wordpress的資料夾(www.jcsoo.com/wordpress)才可以訪問,我想
ESP分割槽和MSR分割槽下怎麼重做GHOST系統
先科普一下兩個分割槽: EFI系統分割槽,即 EFI system partition,簡寫為 ESP。ESP 是一個 FAT16 或 FAT32 格式的物理分割槽,但是其分割槽標識是 EF (十六進