备忘录模式
什么是备忘录模式
备忘录模式是一种行为设计模式, 允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态。(浏览器回退、编辑器撤销与重做、虚拟机生成快照与恢复、游戏悔棋等)
为什么使用备忘录模式
备忘录模式允许你复制对象中的全部状态 (包括私有成员变量), 并将其独立于对象进行保存。 尽管大部分人因为 “撤销” 这个用例才记得该模式, 但其实它在处理事务 (比如需要在出现错误时回滚一个操作) 的过程中也必不可少;备忘录让对象自行负责创建其状态的快照。 任何其他对象都不能读取快照, 这有效地保障了数据的安全性
备忘录模式实现步骤
1.提供一个原发器类:真正要被保存或恢复的对象,其负责创建一个备忘录,可以存储、恢复需要状态信息
2.提供一个备忘录类:用于存储原生器对象的的内部状态,防止外部直接访问原生器对象
3.提供一个管理者类:负责存储备忘录,但不能对备忘录内容进行操作和访问,只能将备忘录传递给其他对象
//下棋退步问题
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Memento {
public:
Memento(string name, pair<int, int> pos) : name(name), pos(pos) {}
string GetName() { return name; }
pair<int, int> GetPos() { return pos; }
void Show() {
cout << name << ":(" << pos.first << "," << pos.second << ")" << endl;
}
private:
string name;
pair<int, int> pos;
};
class Chess {
public:
Chess(string name, pair<int, int> pos) : name(name), pos(pos) {}
void SetChess(string name, pair<int, int> pos) {
this->name = name;
this->pos = pos;
}
Memento* SaveState() { return new Memento(name, pos); }
void Restore(Memento* memento) {
name = memento->GetName();
pos = memento->GetPos();
}
protected:
string name;
pair<int, int> pos;
};
class Caretaker {
public:
//存档
void AddMento(Memento* memento) {
mementos.push_back(memento);
step++;
}
//获取存档
Memento* GetMemento(int index) {
step = index;
return mementos[step];
}
void Show() {
for (int i = 0; i < step; i++) {
cout << "step:" << i + 1 << "手" << endl;
mementos[i]->Show();
}
}
protected:
vector<Memento*> mementos;
static int step;
};
int Caretaker::step = 0;
int main() {
Chess* pc = new Chess("车", {4, 3});
Caretaker* pcaretaker = new Caretaker;
pcaretaker->AddMento(pc->SaveState());
pcaretaker->Show();
cout << endl;
pc->SetChess("马", {8, 8});
pcaretaker->AddMento(pc->SaveState());
pcaretaker->Show();
cout << endl;
pc->SetChess("炮", {6, 6});
pcaretaker->AddMento(pc->SaveState());
pcaretaker->Show();
cout << endl;
pc->Restore(pcaretaker->GetMemento(2));
pcaretaker->Show();
return 0;
}
备忘录模式优缺点
优点
-
你可以在不破坏对象封装情况的前提下创建对象状态快照
-
你可以通过让负责人维护原发器状态历史记录来简化原发器代码
缺点
-
如果客户端过于频繁地创建备忘录, 程序将消耗大量内存
-
负责人必须完整跟踪原发器的生命周期, 这样才能销毁弃用的备忘录
状态模式
什么是状态模式
状态模式是一种行为设计模式, 让你能在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样
为什么使用状态模式
模式建议你将所有特定于状态的代码抽取到一组独立的类中。 这样一来, 你可以在独立于其他状态的情况下添加新状态或修改已有状态, 从而减少维护成本;状态模式让你能够生成状态类层次结构, 通过将公用代码抽取到抽象基类中来减少重复
状态模式实现步骤
1.提供一个上下文环境类:抽象一个客户程序需要的接口,将与状态相关的操作委托给当前的具体状态类对象来处理
2.提供一个抽象给状态类:定义一个接口以封装使用上下文环境的的一个特定状态相关的行为
3.提供一个具体状态类:实现抽象状态定义的接口
//
#include <iostream>
using namespace std;
class Context;
class AbstractState {
public:
virtual void Handle(Context* p) = 0;
};
class ConcreteStateA : public AbstractState {
void Handle(Context* p) { cout << "A....." << endl; }
};
class ConcreteStateB : public AbstractState {
void Handle(Context* p) { cout << "B....." << endl; }
};
class Context {
public:
Context(AbstractState* pstate) : pstate(pstate) {}
void Requst() {
if (pstate) {
pstate->Handle(this);
}
}
void ChangeState(AbstractState* pstate) { this->pstate = pstate; }
private:
AbstractState* pstate;
};
int main() {
AbstractState* pA = new ConcreteStateA;
AbstractState* pB = new ConcreteStateB;
Context* pC = new Context(pA);
pC->Requst();
pC->ChangeState(pB);
pC->Requst();
return 0;
}
状态模式优缺点
优点
-
单一职责原则: 将与特定状态相关的代码放在单独的类中
-
开闭原则: 无需修改已有状态类和上下文就能引入新状态
-
通过消除臃肿的状态机条件语句简化上下文代码
缺点
-
如果状态机只有很少的几个状态, 或者很少发生改变, 那么应用该模式可能会显得小题大作