现在的位置: 首页 > 综合 > 正文

【2013.2.19】对,那个后悔药,给我来三份。——Memento

2018年02月19日 ⁄ 综合 ⁄ 共 3086字 ⁄ 字号 评论关闭

// // // // // // // // //

///2013.2.19

// // // // // // // // //

"曾经有一份真挚的爱情摆在我面前,

我没有去珍惜,

等到失去了才追悔莫及。

如果上天能再给我一次机会,

我一定要说出那三个字——我要当导演。"

时光如梭。

还记得当年那个踢过少林足球,点过秋香,当过喜剧之王的至尊宝,

如今已俨然变身成了胡茬大叔,

做起了导演。

时光如梭。

当年,现在。

也许你一定还有一件事情,

至今后悔当初没那样做。

很可惜,

我们没有月光宝盒。

很幸运,

因为我们有Memento(模式),

可以让我们重头来过。

当然,

这个模式的具体应用更是众所周知——"Undo"。

中文里我们管它叫做"撤销"。

【核心】返回过去的存在状态。

UML图:

这个存在于众多软件之中的功能,

更多时候我们只是简单地按下Ctrl+Z来执行。

但是,

有问题。

大家想过没有,

撤销,这项功能,

该存在于代码结构的什么位置呢?

存在于撤销所在的本体类?

不行,

因为假设我们将本体进行了修改,

按下撤销时,

本体连同撤销一起返回了过去的状态。

(关于这个问题就好像大炮对准自己发射,将炮本身炸坏了然后仍要发射炮弹一样……请原谅笔者无力的解释^_^|||)

存在与外部类?

也不好,

因为毕竟是自己的状态,

怎么能公开呢?

但如果外部类不公开,

自己又怎么将状态传过去呢?

(关于这个问题就好像要将一枚硬币放进封口的瓶子里一样……请再次原谅笔者无力的解释T_T)

等等,

第二个问题我们是不是可以简化成下面这句话?

想办法访问外部类的私有数据。

对了,

我猜你已经想到了。

在C++中,

解决这个问题的关键就是——

友元类。

示例代码:

【大致思路】

Originator是一个系统,状态使用state(string类型)来储存。

Memento是其友元类,用于存储Originator旧状态。

Memento.h

#ifndef _MEMENTO_H_
#define _MEMENTO_H_
#include<string>

class Memento;

using namespace std;
class Originator
{
public:
	Originator(const string& s);
	~Originator();

	void setState(const string& s);
	string getState();
	void backToLastState();

private:
	Memento* mem;
	string state;
};

class Memento
{
public:
	Memento(){}
	~Memento(){}

private:
	friend class Originator;

	//Can't be access by other classes.
	void setOldState(const string& s);
	string getOldState();

	string oldState;
};

#endif

Memento.cpp

#include"Memento.h"

using namespace std;
Originator::Originator(const string& s)
{
	state = s;

	//Create a new memento.
	this->mem = new Memento();
	mem->setOldState(state);
}

Originator::~Originator()
{
	if(mem != nullptr)
		delete mem;
}

void Originator::setState(const string& s)
{
	//Save the old state.
	mem->setOldState(state);

	//Refresh the current state.
	state = s;
}

string Originator::getState()
{
	//Return the current state.
	return state;
}

void Originator::backToLastState()
{
	setState(mem->getOldState());
}

void Memento::setOldState(const string& s)
{
	oldState = s;
}

string Memento::getOldState()
{
	return oldState;
}

main.cpp

#include"Memento.h"
#include<iostream>

using namespace std;

int main()
{
	Originator* typer = new Originator("Zero");

	cout<<"Original state -> "<<typer->getState()<<endl;
	
	//Change state.
	typer->setState("First");
	cout<<"Modified state -> "<<typer->getState()<<endl;

	//Back  to old state.
	typer->backToLastState();
	cout<<"Last state -> "<<typer->getState()<<endl;

	return 0;
}

输出结果:

注意事项:


其实代码本身并没有太多可以讲解的,

毕竟注释已经写得很清楚了。


不过关于撤销还是有点话想说的。

不知道大家有木有注意到过,

撤销也是分为不同类别的。


最常见的是notepad(记事本)的撤销,

如果连续按下去的话,

它只是在当前命令与上一命令之间来回切换而已。

就像这样:A->B->A->B->A->.....


这一类的实现是很容易的,

笔者的撤销就属于这一类。


另一类的撤销就稍微复杂点了,

比如说各个编译器(VS,MonoDeveloper等)的撤销命令,

按下去Ctrl+Z就是一直返回过去的状态,

就像这样:E->D->C->B->A->....

但是相对的,

它也具有"重做"命令(一般是Ctrl+Y),

其与撤销是反操作的,

就像这样:A->B->C->D->E

这一类的撤销的实现稍微复杂点,

涉及到了堆栈的使用。

但每个软件的设计不同,

因此也不能一概而论。

除此之外,

还有一类撤销,

可以随时返回过去任意状态,

最明显的例子就是PS的历史记录了,

点到哪里就返回哪里。

这类撤销的设计方式类似于List或是Hash table.

顺便说一句,

PS具有多种撤销方式,

因为除了历史记录之外它也可以Ctrl+Z。

其实除了PS,

大多数艺术类软件都是同样的设计方式,

例如3ds Max,Mudbox,或是Illustrator,

因为这样非常方便进行艺术创作(想一想为什么)。

说一点题外话吧,

——

其实有时候真想在自己肚子上安装一个撤销按钮,

一按下去,

扑哧,就返回过去了。

#Problems

但这种想法是很超前的,

因为现实世界这个系统太过于庞大,

目前还没有一个处理器能处理万物的行为,

还有没有一个硬盘可以存储万物的状态,

更没有一个强大的系统可以完全模拟这一切。

尽管如此,

有时候笔者也会站在洒满阳光的窗台前,

望着灿烂了整个暮空的夕阳,

思考这种系统的设计方式。

目前最初级的想法是创建一个4纬位数组,

三个轴存储空间位运算,

最后一个存储时间的位运算,

然后每隔一段时间将现有数组与原数组进行对比,

发生改变的部分push到一个状态栈中去,

然后当按下肚子上的撤销按钮时,

就从此状态栈中pop一个状态出来,

将自己现有状态替换掉。

但是实现起来有困难,

困难是什么呢?

goto #Problems;

郭德纲都上春晚了,

笔者这个单纯的想法还没能实现,

不得不说这是人生一大憾事。

所以,

在发明出这种系统(机器?)之前,

还是珍惜当下吧。

不要让自己有任何遗憾,

这样就不会想穿梭时光了。

抱歉!评论已关闭.