引言
当我们和朋友下棋的时候,我们很多情况下会发现下了一步臭棋,这时候就会和朋友开玩笑要悔棋,即撤回刚刚下的一步棋。在程序中,很多时候也会出错,我们也希望程序可以恢复出错前的状态,这就需要备忘录模式来实现。
1.概念
备忘录模式(Memento Pattern):在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。它是一种对象行为型模式,其别名为Token。
2.模式结构
3.模式分析
Originator:原发器,是一个普通类,可以创建一个备忘录,并存储它的当前内部状态,也可以使用备忘录来恢复其内部状态,一般将需要保存内部状态的类设计为原发器。核心代码如下:
public class Originator {private String state;public Originator() {}// 创建一个备忘录对象public Memento createMemento() {return new Memento(this);}// 根据备忘录对象恢复原发器状态public void restoreMemento(Memento m) {state = m.state;}public void setState(String state) {this.state = state;}public String getState() {return this.state;}}
Memento:备忘录,存储原发器的内部状态,根据原发器来决定保存哪些内部状态。备忘录的设计一般可以参考原发器的设计,根据实际需要确定备忘录类中的属性。需要注意的是,除了原发器本身与负责人类之外,备忘录对象不能直接供其他类使用,原发器的设计在不同的编程语言中实现机制会有所不同。核心代码如下:
class Memento{private String state;public Memento(Originator o) {state = o.getState();}public void setState(String state) {this.state = state;}public String getState() {return this.state;}}
Caretaker:负责人,负责人又称为管理者,它负责保存备忘录,但是不能对备忘录的内容进行操作或检查。在负责人类中可以存储一个或多个备忘录对象,它只负责存储对象,而不能修改对象,也无须知道对象的实现细节。核心代码如下:
public class Caretaker {private Memento memento;public Memento getMemento() {return memento;}public void setMemento(Memento memento) {this.memento = memento;}}
4.具体实例分析
ChessOriginator:象棋原发器,定义象棋的类型和位置属性,以及相关的构造方法、Getter和Setter方法,原发器需要包含创建备忘录的方法和状态恢复方法。具体代码如下:
//象棋原生类public class ChessOriginator {private String label;private int x;private int y;public ChessOriginator(String label, int x, int y) {this.label = label;this.x = x;this.y = y;}public String getLabel() {return label;}public void setLabel(String label) {this.label = label;}public int getX() {return x;}public void setX(int x) {this.x = x;}public int getY() {return y;}public void setY(int y) {this.y = y;}// 创建一个备忘录对象public ChessMemento createMemento() {return new ChessMemento(this.label,this.x,this.y);}// 根据备忘录对象恢复原发器状态public void restoreMemento(ChessMemento chessMemento) {this.label = chessMemento.getLabel();this.x = chessMemento.getX();this.y = chessMemento.getY();}@Overridepublic String toString() {return "棋子:" + label + " 当前位置:(" + x + ", " + y + ")";}}
ChessMemento:象棋备忘录,负责记录象棋的某时刻状态。具体代码如下:
//象棋备忘录class ChessMemento{private String label;private int x;private int y;public ChessMemento(String label, int x, int y) {this.label = label;this.x = x;this.y = y;}public String getLabel() {return label;}public void setLabel(String label) {this.label = label;}public int getX() {return x;}public void setX(int x) {this.x = x;}public int getY() {return y;}public void setY(int y) {this.y = y;}}
MementoCaretaker:备忘录管理类,负责管理象棋备忘录,比如获得上一时刻的象棋状态和保存上一时刻的象棋状态。具体代码如下:
//负责人:象棋备忘录管理类public class MementoCaretaker {private ChessMemento chessMemento;public ChessMemento getMemento() {return chessMemento;}public void setMemento(ChessMemento chessMemento) {this.chessMemento = chessMemento;}}Client:客户端,模拟象棋悔棋。具体代码如下:public class Client {public static void main(String[] args) {MementoCaretaker mementoCaretaker = new MementoCaretaker();ChessOriginator chessOriginator = new ChessOriginator("炮",2,2);System.out.println(chessOriginator);mementoCaretaker.setMemento(chessOriginator.createMemento());//保存上一步状态chessOriginator.setX(4);System.out.println(chessOriginator);//悔棋System.out.println("求求你让我悔棋吧!!!");chessOriginator.restoreMemento(mementoCaretaker.getMemento());System.out.println(chessOriginator);}}
运行代码,结果如下:
5.优缺点
主要优点如下:
(1)它提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。
(2)备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。
主要缺点如下:
(1)资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源。
6.适用情况
(1)保存一个对象在某一个时刻的全部状态或部分状态,这样以后需要时它能够恢复到先前的状态,实现撤销操作。
(2)防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象。