目录
前言:
今天我们一起来认识一下:备忘录模式;
在软件开发中,我们经常会遇到需要保存和恢复对象状态的场景,比如撤销操作、历史记录功能等。备忘录模式(Memento Pattern)就是一种能够很好地解决这类问题的设计模式。备忘录模式能够在不破坏封装性的情况下,捕获一个对象的内部状态,并在之后将该对象恢复到原先的状态。这种模式提供了一种轻松的方式来支持撤销和恢复功能,同时也能够有效地管理对象的状态历史。
备忘录模式主要涉及三个角色:原发器(Originator)、备忘录(Memento)和负责人(Caretaker)。原发器负责创建备忘录并在需要时恢复状态,备忘录负责存储原发器的内部状态,而负责人则负责保存备忘录但不能对其内容进行操作。这种角色分工清晰,能够有效地实现对象状态的保存和恢复。
通过备忘录模式,我们可以轻松实现撤销和恢复功能,同时也能够简化原发器的代码,提高代码的可维护性。备忘录模式在诸如文本编辑器、绘图软件、游戏存档等场景中有着广泛的应用,为我们提供了一种灵活而强大的工具,帮助我们更好地管理对象的状态历史。
在接下来的内容中,我们将深入探讨备忘录模式的原理、应用场景、优缺点以及实际案例,希望能够帮助大家更好地理解和应用备忘录模式,从而在实际的软件开发中发挥其巨大的作用。
一、原理及示例
备忘录模式是一种行为设计模式,它允许在不暴露对象实现细节的情况下保存和恢复对象的内部状态。这种模式通常用于需要记录和恢复对象状态的情况,例如撤销操作、历史记录等。
在备忘录模式中,通常包含以下几个角色:
- 发起人(Originator):负责创建备忘录,用于记录当前状态,并可以使用备忘录来恢复自身状态。
- 备忘录(Memento):存储发起人的内部状态,可以防止发起人以外的对象访问备忘录。
- 管理者(Caretaker):负责存储备忘录,但不对备忘录的内容进行操作。通常用于存储多个备忘录,以支持撤销和恢复操作。
下面是一个简单的C++示例代码,演示了备忘录模式的用法:
#include <iostream>
#include <string>
// 备忘录类
class Memento {
public:
Memento(const std::string& state) : state_(state) {}
std::string getState() const { return state_; }
private:
std::string state_;
};
// 发起人类
class Originator {
public:
void setState(const std::string& state) {
state_ = state;
std::cout << "Current state: " << state_ << std::endl;
}
Memento createMemento() {
return Memento(state_);
}
void restoreMemento(const Memento& memento) {
state_ = memento.getState();
std::cout << "State restored: " << state_ << std::endl;
}
private:
std::string state_;
};
// 管理者类
class Caretaker {
public:
void addMemento(const Memento& memento) {
mementos_.push_back(memento);
}
Memento getMemento(int index) {
return mementos_[index];
}
private:
std::vector<Memento> mementos_;
};
int main() {
Originator originator;
Caretaker caretaker;
originator.setState("State 1");
caretaker.addMemento(originator.createMemento());
originator.setState("State 2");
caretaker.addMemento(originator.createMemento());
originator.restoreMemento(caretaker.getMemento(0));
return 0;
}
在上面的示例中,Originator类表示发起人,Memento类表示备忘录,Caretaker类表示管理者。通过调用createMemento()方法创建备忘录,并通过restoreMemento()方法恢复状态。
二、结构图
备忘录模式的结构图包括以下几个关键元素:
-
发起人(Originator):负责创建备忘录,并能够将其当前状态保存到备忘录中,或从备忘录中恢复状态。
-
备忘录(Memento):负责存储发起人的内部状态。它可以防止发起人以外的对象访问备忘录的状态。
-
管理者(Caretaker):负责存储备忘录,但不对备忘录的内容进行操作。通常用于存储多个备忘录,以支持撤销和恢复操作。
下面是备忘录模式的结构图示例:
+-------------------+ +-------------------+
| Originator | | Memento |
+-------------------+ +-------------------+
| - state: State | | - state: State |
+-------------------+ +-------------------+
| + setState() | | + getState() |
| + createMemento() | +-------------------+
| + restoreMemento()|
+-------------------+
|
|
|
|
+-------------------+
| Caretaker |
+-------------------+
| - mementos: list |
+-------------------+
| + addMemento() |
| + getMemento() |
+-------------------+
在上面的结构图中,Originator类表示发起人,Memento类表示备忘录,Caretaker类表示管理者。发起人通过createMemento()方法创建备忘录,并通过restoreMemento()方法恢复状态。管理者负责存储备忘录,以支持撤销和恢复操作。
三、使用场景:
备忘录模式通常在以下情况下使用:
-
需要实现撤销和恢复功能:当需要在程序中实现撤销和恢复功能时,备忘录模式可以帮助保存对象的状态,并在需要时进行恢复。
-
需要保存对象的历史状态:当需要保存对象的历史状态,以便后续分析或回滚时,备忘录模式可以用于存储对象的状态历史。
-
需要实现快照功能:当需要对对象的状态进行快照,并能够在需要时恢复到特定的状态时,备忘录模式可以提供这样的功能。
-
需要实现事务回滚:在需要实现事务回滚的系统中,备忘录模式可以用于保存事务执行前的状态,以便在回滚时恢复到之前的状态。
-
需要实现状态检查点:在需要定期保存系统状态的应用程序中,备忘录模式可以用于创建状态检查点,以便在需要时恢复到特定的检查点状态。
总之,备忘录模式适用于需要保存对象状态并能够在需要时恢复到先前状态的情况,特别是在需要实现撤销、恢复、快照、历史状态记录等功能的应用程序中备忘录模式非常有用。
场景1:需要实现撤销和恢复功能
#include <iostream>
#include <string>
#include <vector>
// 备忘录类
class Memento {
public:
Memento(const std::string& state) : state_(state) {}
std::string getState() const { return state_; }
private:
std::string state_;
};
// 发起人类
class Originator {
public:
void setState(const std::string& state) {
state_ = state;
}
std::string getState() const {
return state_;
}
Memento createMemento() {
return Memento(state_);
}
void restoreMemento(const Memento& memento) {
state_ = memento.getState();
}
private:
std::string state_;
};
// 管理者类
class Caretaker {
public:
void addMemento(const Memento& memento) {
mementos_.push_back(memento);
}
Memento getMemento(int index) {
return mementos_[index];
}
private:
std::vector<Memento> mementos_;
};
int main() {
Originator originator;
Caretaker caretaker;
originator.setState("State 1");
caretaker.addMemento(originator.createMemento());
originator.setState("State 2");
caretaker.addMemento(originator.createMemento());
std::cout << "Current state: " << originator.getState() << std::endl;
originator.restoreMemento(caretaker.getMemento(0));
std::cout << "Restored state: " << originator.getState() << std::endl;
return 0;
}
场景2:
当需要保存对象的历史状态时,可以使用备忘录模式。下面是一个详细的C++示例代码:
#include <iostream>
#include <string>
#include <vector>
// 备忘录类
class Memento {
public:
Memento(const std::string& state) : state_(state) {}
std::string getState() const {
return state_;
}
private:
std::string state_;
};
// 原发器类
class Originator {
public:
void setState(const std::string& state) {
state_ = state;
}
std::string getState() const {
return state_;
}
Memento createMemento() {
return Memento(state_);
}
void restoreFromMemento(const Memento& memento) {
state_ = memento.getState();
}
private:
std::string state_;
};
// 管理者类
class Caretaker {
public:
void addMemento(const Memento& memento) {
mementos_.push_back(memento);
}
Memento getMemento(int index) {
return mementos_[index];
}
private:
std::vector<Memento> mementos_;
};
int main() {
Originator originator;
Caretaker caretaker;
originator.setState("State 1");
caretaker.addMemento(originator.createMemento());
originator.setState("State 2");
caretaker.addMemento(originator.createMemento());
std::cout << "Current state: " << originator.getState() << std::endl;
originator.restoreFromMemento(caretaker.getMemento(0));
std::cout << "Restored state: " << originator.getState() << std::endl;
return 0;
}
在这个示例中,我们定义了Memento
、Originator
和Caretaker
三个类,分别代表备忘录、原发器和管理者。原发器类中的createMemento
方法用于创建备忘录,restoreFromMemento
方法用于根据备忘录恢复对象的状态。管理者类用于保存和获取备忘录。在main
函数中,我们创建了一个原发器对象和一个管理者对象,分别设置对象的状态并保存历史状态,然后恢复到之前的状态并输出。这展示了备忘录模式在保存对象的历史状态时的使用方式。
场景3:
当需要实现快照功能时,可以使用备忘录模式。下面是一个详细的C++示例代码:
#include <iostream>
#include <string>
// 备忘录类
class Memento {
public:
Memento(const std::string& state) : state_(state) {}
std::string getState() const {
return state_;
}
private:
std::string state_;
};
// 原发器类
class Originator {
public:
void setState(const std::string& state) {
state_ = state;
}
std::string getState() const {
return state_;
}
Memento createSnapshot() {
return Memento(state_);
}
void restoreFromSnapshot(const Memento& snapshot) {
state_ = snapshot.getState();
}
private:
std::string state_;
};
int main() {
Originator originator;
originator.setState("State 1");
Memento snapshot1 = originator.createSnapshot();
originator.setState("State 2");
Memento snapshot2 = originator.createSnapshot();
std::cout << "Current state: " << originator.getState() << std::endl;
originator.restoreFromSnapshot(snapshot1);
std::cout << "Restored state from snapshot 1: " << originator.getState() << std::endl;
return 0;
}
在这个示例中,我们定义了Memento
和Originator
两个类,分别代表备忘录和原发器。原发器类中的createSnapshot
方法用于创建快照(即备忘录),restoreFromSnapshot
方法用于根据快照恢复对象的状态。在main
函数中,我们创建了一个原发器对象,设置对象的状态并创建快照,然后再次设置状态并创建另一个快照。接着输出当前状态,然后恢复到第一个快照的状态并输出。这展示了备忘录模式在实现快照功能时的使用方式。
场景4:
当需要实现撤销/恢复功能时,可以使用备忘录模式。下面是一个详细的C++示例代码:
#include <iostream>
#include <string>
#include <vector>
// 备忘录类
class Memento {
public:
Memento(const std::string& state) : state_(state) {}
std::string getState() const {
return state_;
}
private:
std::string state_;
};
// 原发器类
class Originator {
public:
void setState(const std::string& state) {
state_ = state;
}
std::string getState() const {
return state_;
}
Memento createMemento() {
return Memento(state_);
}
void restoreFromMemento(const Memento& memento) {
state_ = memento.getState();
}
private:
std::string state_;
};
// 管理者类
class Caretaker {
public:
void addMemento(const Memento& memento) {
mementos_.push_back(memento);
}
Memento getMemento(int index) {
return mementos_[index];
}
private:
std::vector<Memento> mementos_;
};
int main() {
Originator originator;
Caretaker caretaker;
originator.setState("State 1");
caretaker.addMemento(originator.createMemento());
originator.setState("State 2");
caretaker.addMemento(originator.createMemento());
std::cout << "Current state: " << originator.getState() << std::endl;
originator.restoreFromMemento(caretaker.getMemento(0));
std::cout << "Restored state: " << originator.getState() << std::endl;
return 0;
}
在这个示例中,我们定义了Memento
、Originator
和Caretaker
三个类,分别代表备忘录、原发器和管理者。原发器类中的createMemento
方法用于创建备忘录,restoreFromMemento
方法用于根据备忘录恢复对象的状态。管理者类用于保存和获取备忘录。在main
函数中,我们创建了一个原发器对象和一个管理者对象,分别设置对象的状态并保存历史状态,然后恢复到之前的状态并输出。这展示了备忘录模式在实现撤销/恢复功能时的使用方式。
场景5:
第五种使用场景是在需要实现状态的持久化和恢复时使用备忘录模式。下面是一个详细的C++示例代码:
#include <iostream>
#include <string>
#include <fstream>
// 备忘录类
class Memento {
public:
Memento(const std::string& state) : state_(state) {}
std::string getState() const {
return state_;
}
private:
std::string state_;
};
// 原发器类
class Originator {
public:
void setState(const std::string& state) {
state_ = state;
}
std::string getState() const {
return state_;
}
Memento createMemento() {
return Memento(state_);
}
void restoreFromMemento(const Memento& memento) {
state_ = memento.getState();
}
void saveToFile(const std::string& filename) {
std::ofstream file(filename);
file << state_;
file.close();
}
void loadFromFile(const std::string& filename) {
std::ifstream file(filename);
std::getline(file, state_);
file.close();
}
private:
std::string state_;
};
int main() {
Originator originator;
originator.setState("State 1");
originator.saveToFile("state.txt");
originator.setState("State 2");
originator.loadFromFile("state.txt");
std::cout << "Restored state from file: " << originator.getState() << std::endl;
return 0;
}
在这个示例中,我们定义了Memento
和Originator
两个类,分别代表备忘录和原发器。原发器类中的createMemento
方法用于创建备忘录,restoreFromMemento
方法用于根据备忘录恢复对象的状态。除此之外,我们还为原发器类添加了saveToFile
和loadFromFile
方法,用于将状态保存到文件和从文件中加载状态。在main
函数中,我们创建了一个原发器对象,设置对象的状态并将状态保存到文件,然后再次设置状态并从文件中加载状态。接着输出从文件中恢复的状态,这展示了备忘录模式在实现状态的持久化和恢复时的使用方式。
四:优缺点:
备忘录模式是一种有用的设计模式,它具有以下优点和缺点:
优点:
- 支持撤销和恢复:备忘录模式可以轻松地实现撤销和恢复功能,因为它允许在不破坏封装性的情况下保存和恢复对象的状态。
- 简化原发器:备忘录模式将状态保存和恢复的逻辑与原发器分离,使得原发器的代码更加简洁和清晰。
- 支持多次撤销:备忘录模式可以保存多个状态的历史记录,从而支持多次撤销操作。
缺点:
- 内存消耗:如果需要保存大量的状态历史记录,可能会占用大量的内存空间。
- 性能开销:在保存和恢复大量状态的情况下,可能会带来一定的性能开销。
总的来说,备忘录模式是一种非常有用的设计模式,特别适用于需要支持撤销和恢复功能的场景。然而,在使用备忘录模式时,需要权衡内存消耗和性能开销,并根据具体的业务需求进行合理的设计和优化。
五、常见面试题:
当面试备忘录模式时,以下是一些可能的面试问题以及相应的答案:
-
什么是备忘录模式? 答:备忘录模式是一种行为型设计模式,用于在不破坏封装性的情况下保存和恢复对象的内部状态。
-
备忘录模式中的三个主要角色是什么? 答:备忘录模式中的三个主要角色包括原发器(Originator)、备忘录(Memento)和负责人(Caretaker)。
-
请解释原发器、备忘录和负责人在备忘录模式中的作用。 答:原发器负责创建备忘录并在需要时恢复状态,备忘录负责存储原发器的内部状态,而负责人负责保存备忘录但不能对其内容进行操作。
-
什么时候应该使用备忘录模式? 答:备忘录模式适用于需要支持撤销和恢复功能的场景,或者需要保存和恢复对象状态的场景。
-
请举例说明备忘录模式在实际项目中的应用。 答:备忘录模式在文本编辑器、绘图软件、游戏中的存档功能等场景中有着广泛的应用。
-
备忘录模式的优点是什么? 答:备忘录模式可以轻松实现撤销和恢复功能,简化了原发器的代码,同时支持对象状态的备份和恢复。
-
备忘录模式的缺点是什么? 答:备忘录模式可能会占用大量的内存空间,特别是需要保存大量状态历史记录时,可能会带来一定的性能开销。
-
在备忘录模式中,如何防止原发器以外的对象访问备忘录的状态? 答:可以使用内部类的方式实现备忘录类,使得只有原发器可以访问备忘录的状态。
-
备忘录模式和命令模式有何区别? 答:备忘录模式用于保存和恢复对象的状态,而命令模式用于将请求封装成对象,从而支持撤销、重做等操作。
-
如何在备忘录模式中实现多级撤销功能? 答:可以使用栈或者链表等数据结构来保存多个备忘录,从而实现多级撤销功能。
----------------------------------------------------------------------------------
5
+---+
3 | | |+ | +---|
+ | | 3 | 6 | | + | +
+ + | | | + | | + | +
+ + | +---+ | + +++++ | | + | +
+ + | + | | +----+ | | | | + | +
+ + 3 | + | | + + 2 | | 2 | | + | +
+ + | + | | + + | | | | + | +
+ +---+ + | | + + ----+ | +---+ | | + | +
+ | + | | + + | | | | | | + | +
+ 1 | + | 8 | + + 1 | | | 1 | | 1 | | + | +
+ | + | | + + | | | | | | | + | +
+ +---+ + +---+ + ++---+ | +---+ +---+ | + | +
+ | + | + | | | | |+ | +
+0 | + | 0 + 0 | | | 0 | |+ | +
+ | + | + | | | | |+ | +
+---+ + +-------+ +---+| +|+ | +
+ + | +
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + |+----16 17 18