"我不能預(yù)見每個人的未來,我只能預(yù)見我自己的,而且只能預(yù)見兩分鐘"——尼古拉斯.凱奇《驚魂下一秒》2007
無論人寫字,畫畫一樣然走,我們常常有筆誤不可避免,
回到過去的某個修改點(diǎn)戏挡,做出不同的修改芍瑞,并繼續(xù),
在程序設(shè)計的概念里褐墅,這常常指版本管理拆檬,版本管理保存了每一次(所有)修改的歷史,不同時間線妥凳,還有合并
而編輯器的竟贯,undo/redo, 則有幾點(diǎn)簡化,
- 只保留一部分修改記錄——通常我們只關(guān)心近期的修改逝钥,
- undo/redo 為逆操作屑那,但undo不銷毀歷史,而任何undo之后的修改,則會銷毀redo序列
這像是《驚魂下一秒》里的故事持际,修正有限歷史沃琅,并讓下一秒沖刷掉未來。
undo/redo模式选酗,即為阵难,維護(hù)一定長度的修改點(diǎn)隊列,并在所有歷史修改點(diǎn)里芒填,進(jìn)行版本切換.
以下我實(shí)現(xiàn)了一個簡單的undo/redo呜叫,版本管理,
//版本數(shù)據(jù)庫殿衰,為了實(shí)現(xiàn)備份朱庆,data須可clone
public class DataBackup<Data> where Data : class, ICloneable, new()
{
//版本隊列
List<Data> mDataBackup = new List<Data>() { new Data() };
const int MAX_LEN = 15;//版本歷史限制
int dataIdx = 0;//版本號
public Data data
{
get
{
return mDataBackup[dataIdx];
}
}
private void trim()
{
mDataBackup.RemoveRange(dataIdx + 1, mDataBackup.Count - dataIdx - 1);
}
//備份
public void backup()
{
trim();//消除所有未來版本
mDataBackup.Add(data.Clone() as Data);
if (dataIdx > MAX_LEN)
{
mDataBackup.RemoveAt(0);
}
dataIdx = mDataBackup.Count - 1;
}
//撤銷
public void undo()
{
dataIdx--;
if (dataIdx < 0)
dataIdx = 0;
}
//重做
public void redo()
{
dataIdx++;
if (dataIdx >= mDataBackup.Count)
dataIdx = mDataBackup.Count - 1;
}
}
可以看到,這樣簡單的undo/redo已經(jīng)足夠工作闷祥。
實(shí)現(xiàn)更精巧的 redo/undo功能娱颊,你需要考慮以下問題:
當(dāng)data特別大,每一個版本僅需要保存版本增量凯砍,因此需要實(shí)現(xiàn),
gain(data_v1, diff) == data_v2
revert(data_v2, diff) == data_v1MVC模式下箱硕,不一定是對于modeller的數(shù)據(jù)集, 每次備份能可以是controller 操作集 action list,因此control需要實(shí)現(xiàn) 一組可逆接口悟衩,例如剧罩,
addEntity/removeEntiy
changeDeltaPos(x, y) / changeDeltaPos(-x, -y)
而現(xiàn)實(shí)中,對于有些編輯器的實(shí)現(xiàn)來說座泳,效率并不是一個嚴(yán)重的問題惠昔,簡單則是更為重要的。