设计模式初探-备忘录模式

时间:2022-10-01 22:44:34

备忘录模式(MEMENTO),又称Token,通过在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以待需要时将该对象恢复到原先保存的状态,属于对象行为型模式。备忘录模式很适合实现软件中的撤销和重做功能,在字处理软件,图像编辑软件,数据库管理软件中十分常见。游戏里也常常可以使用备忘录模式来保存游戏的状态,如游戏失败前的状态,游戏暂停时的状态,以备玩家重新接着死亡前的关卡继续或从暂停状态恢复,这样使游戏更人性化。

一、使用场景

1、保存一个对象在某个时刻的全部或部分状态,以待需要时重新恢复到该状态。

2、防止通过接口来让其他对象直接操作对象的内部状态,避免暴露对象的实现细节并破坏对象的封装性。

二、UML图

设计模式初探-备忘录模式

三、Java实现

package study.patterns.memento;
/**
* 备忘录模式很适合实现重做(Redo)和撤销(Undo)。
* 备忘录是一个很特殊的对象,只有原发器对它拥有控制的权力,负责人只负责管理,
* 而其他类无法访问到备忘录,因此我们需要对备忘录进行封装。
* 为了实现对备忘录对象的封装,需要对备忘录的调用进行控制,对于原发器而言,
* 它可以调用备忘录的所有信息,允许原发器访问返回到先前状态所需的所有数据;
* 对于负责人而言,只负责备忘录的保存并将备忘录传递给其他对象;
* 对于其他对象而言,只需要从负责人处取出备忘录对象并将原发器对象的状态恢复,而无须关心备忘录的保存细节。
* 理想的情况是只允许生成该备忘录的那个原发器访问备忘录的内部状态
* @author qbg
*/
public class MementoPattern {

public static void main(String[] args) {
Caretaker taker = new Caretaker();
Originator or = new Originator();
or.setState("状态1");
display(or);
//保存状态1
taker.setMemento(or.createMemento());
or.setState("状态2");
display(or);
System.out.println("从备忘录恢复状态....");
or.restoredFromMemento(taker.getMemento());
display(or);
}

public static void display(Originator or){
System.out.println("当前状态:"+or.getState());
}
}
/**
*原发器:一个普通类,可以创建一个备忘录,并存储它的当前内部状态,
*也可以使用备忘录来恢复其内部状态,一般将需要保存内部状态的类设计为原发器.
*/
class Originator{
private String state;

/**
* 根据当前原发器创建备忘录
*/
public Memento createMemento(){
return new Memento(this);
}
/**
* 从备忘录里恢复状态
*/
public void restoredFromMemento(Memento m){
this.state = m.getState();
}
public String getState(){
return state;
}
public void setState(String state){
this.state = state;
}
}
/**
*备忘录:存储原发器的内部状态,根据原发器来决定保存哪些内部状态。
*备忘录的设计一般可以参考原发器的设计,根据实际需要确定备忘录类中的属性。
*需要注意的是,除了原发器本身与负责人类之外,备忘录对象不能直接供其他类使用,
*更不能让其他类修改备忘录里的状态。
*通过将备忘录与原发器定义在同一包级别来实现封装。
*/
class Memento{
private String state;

public Memento(Originator or){
this.state = or.getState();
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
/**
*负责人:负责人又称为管理者,它负责保存备忘录,但是不能对备忘录的内容进行操作或检查。
*在负责人类中可以存储一个或多个备忘录对象,它只负责存储对象,而不能修改对象,也无须知道对象的实现细节。
*/
class Caretaker{
private Memento memento;

public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
运行结果:

当前状态:状态1
当前状态:状态2
从备忘录恢复状态....
当前状态:状态1
四、模式优缺点

优点:

1、保持封装边界。备忘录可以避免暴露一些只应由原发器管理却又必须存储在原发器之外的信息(原发器的某个或某些临时状态)。通过备忘录将原发器Originator内部信息对其他对象屏蔽起来,从而保持了封装边界。

2、简化了原发器。备忘录模式通过将原发器不同的内部状态版本封装到对应的备忘录中,并将这些备忘录对象的存储交给负责人来简化原发器的设计和实现。

3、提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤。当新的状态无效或者出现错误时,可以使用暂时存储起来的备忘录将状态复原。

缺点:

1、某些情况下使用备忘录代价会很高。如果原发器在生成备忘录时必须拷贝并存储大量的信息,或客户非常频繁地创建备忘录和恢复原发器状态,可能会导致非常大的开销,这些情况下不适合使用备忘录模式。
2、维护备忘录的潜在代价。由于系统不知道维护备忘录的多少,所以可能会导致存储大量的备忘录对象。这可以通过限制系统存储备忘录对象的数量来解决。另外系统要时刻防止其他对象修改备忘录状态,增加系统设计的复杂性。