设计模式–备忘录模式
备忘录模式
定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可对该对象恢复到原来的状态。
在玩象棋或者五子棋的时候,常会遇到要悔棋情况,一步二步还好,可以记下来,但是如果从一开始就把所有走的步骤都记下来,属实很困难(不仅仅下棋,就比如我们编程所用的编译器,excel,word等等,都有撤销还原的功能)。现在如果我们用编程,可以想象用一个足够大的数组将走的所有步骤都保存在起来,记录当前的下标,每次悔棋还原,就将上标前移读取上一次的状态。面向过程我想在座的各位都是神仙选手,所有我们不谈,直接上备忘录模式。
看下uml图:
这个模式由三部分组成:
- Originator(发起人):负责创建一个备忘录memeto,用于记录当前时刻的内部状态,并可以使用备忘录恢复内部状态。
- Memeto(备忘录):负责存储Originator对象的内部状态,并且防止Originator以外的对象访问备忘录。备忘录有两个接口,CreatMemeto只能看到备忘录的窄接口,他只能将备忘录转递给其他对象。Originator能看到宽接口,允许它访问返回先前状态所需的数据。
- CreateMemeto(管理者):负责保存好备忘录Memeto,不能对备忘录的内容进行操作或者检查,可以通过这个接口添加新的内容。
具体实现(c++)
建议copy到本地,跑一跑。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<list>
#include<string>
using namespace std;
class Memeto{
private:
int x;
int y;
string label;
public:
Memeto(){}
Memeto(int x,int y,string label):x(x),y(y),label(label){};
int getx(){ return this->x;}
int gety(){ return this->y;}
string getlabel(){ return this->label;}
};
class Originator{
private:
int x;
int y;
string label;
public:
Originator(){}
Originator(int x,int y,string label):x(x),y(y),label(label){};
Memeto* save(){
return new Memeto(this->x,this->y,this->label);
}
void restore(Memeto* memeto){
this->x = memeto->getx();
this->y = memeto->gety();
this->label = memeto->getlabel();
}
void setx(int x){this->x = x;}
void sety(int y){this->y = y;}
void show(){
cout<<"当前位置下子"<<": "<<this->label<<"<"<<this->x<<","<<this->y<<">!!!"<<endl;
}
};
class CreatMemeto{
private:
list<Memeto*> memetos;
list<Memeto*>::iterator it;
int index;
int nowindex;
public:
void addmemeto(Memeto* memeto){
memetos.push_back(memeto);
index++;
nowindex++;
}
Memeto* getmemeto(int i){
int j=0;
for(it=memetos.begin();it!=memetos.end();it++){
j++;
if(j==i){
return *it;
}
}
}
void play(Originator* Or){
addmemeto(Or->save());
Or->show();
}
void regret(Originator* Or){
if(nowindex<=1){
cout<<"已经没有步数,无法悔棋"<<endl;
}
else{
cout<<"悔棋"<<endl;
nowindex--;
Or->restore(getmemeto(nowindex));
Or->show();
}
}
void revokeregret(Originator* Or){
cout<<"撤销悔棋"<<endl;
nowindex++;
if(nowindex > index){
cout<<"撤销悔棋失败"<<endl;
}
else{
Or->restore(getmemeto(nowindex));
Or->show();
}
}
};
int main(){
CreatMemeto *Memetos = new CreatMemeto();
Originator* temp = new Originator(1,1,"黑子");
Memetos->play(temp);
temp->setx(2);
Memetos->play(temp);
temp->sety(2);
Memetos->play(temp);
Memetos->regret(temp);
Memetos->revokeregret(temp);
Memetos->regret(temp);
Memetos->revokeregret(temp);
Memetos->regret(temp);
Memetos->regret(temp);
Memetos->regret(temp);
Memetos->regret(temp);
Memetos->revokeregret(temp);
Memetos->revokeregret(temp);
Memetos->revokeregret(temp);
}
//运行结果
/*
当前位置下子: 黑子<1,1>!!!
当前位置下子: 黑子<2,1>!!!
当前位置下子: 黑子<2,2>!!!
悔棋
当前位置下子: 黑子<2,1>!!!
撤销悔棋
当前位置下子: 黑子<2,2>!!!
悔棋
当前位置下子: 黑子<2,1>!!!
撤销悔棋
当前位置下子: 黑子<2,2>!!!
悔棋
当前位置下子: 黑子<2,1>!!!
悔棋
当前位置下子: 黑子<1,1>!!!
已经没有步数,无法悔棋
已经没有步数,无法悔棋
撤销悔棋
当前位置下子: 黑子<2,1>!!!
撤销悔棋
当前位置下子: 黑子<2,2>!!!
撤销悔棋
撤销悔棋失败
*/
特点
优点:
Memeto模式比较适用于功能比较复杂的,但需要维护或者记录属性的类,或者需要保存的属性只是众多属性中的一部分时,Originator可以根据保存的Memeto信息还原到前一状态。备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。
缺点:开局一把刀,剩下。。。。。开局我们说,可以用一个足够大的数组保存我们之前操作的状态,足够大足够大,到底多大。不知道,反正就很费存储空间。