java memento_Java设计模式之-备忘录模式(Memento)

看到“备忘录”这个名字的时候,我基本上不知道这个模式需要做的事情。而后又翻看了一下GoF的书,它的Intent是这个样子的:

Memento: Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later.

在不破坏封装的前提下,获得对象的内部状态并外部化,用于以后的状态恢复行为。看这个解释,可以发现备忘录模式的要点有两方面:

外部化(externalize);

恢复(restore);

为了要恢复,所以得进行外部化。那么如何进行外部化也就直接关系到如何恢复。我们来举两个常见的场景,看看一般我们可以如何进行外部化和恢复。

撤销和重做

撤销(Undo)和重做(Redo)是我们在日常学习工作中经常用到的操作。为了要实现撤销和重做,我们要将每一个动作的状态保存起来(外部化),在适当的时候拿出来进行恢复。

我们首先定义两个接口,定义撤销和重做的相关方法。

public interface Undoable {

public void undo();

public boolean canUndo();

}

public interface Redoable {

public void redo();

public boolean canRedo();

}

然后我们写一个类来实现这两个接口。假设我们要写一个TextField,这个控件可以进行移动,默认的锚点是左上角的点,然后还可以往这个控件中增加字符。

package com.designpattern.memento;

import java.awt.*;

import java.util.ArrayDeque;

import java.util.Deque;

public class TextField implements Undoable, Redoable, Cloneable {

private Point currentPoint = new Point(0, 0);

private StringBuilder text = new StringBuilder();

private Deque historyStack = new ArrayDeque<>();

private Deque futureStack = new ArrayDeque<>();

//移动文本框

public void move(int x, int y) {

restoreState();

currentPoint.move(x, y);

}

//在文本框中输入文字

public void append(String str) {

restoreState();

text.append(str);

}

//保存状态

private void restoreState() {

historyStack.push((TextField) clone());

}

@Override

public void redo() {

if (canRedo() == false) {

System.out.println("No redo state found......");

return;

}

TextField instance = futureStack.pop();

this.currentPoint = new Point(instance.currentPoint);

this.text = new StringBuilder(instance.text.toString());

this.historyStack.push(instance);

}

@Override

public boolean canRedo() {

return futureStack.size() != 0;

}

@Override

public void undo() {

if (canUndo() == false) {

System.out.println("No undo state found......");

return;

}

this.futureStack.push((TextField) clone());

TextField instance = historyStack.pop();

this.currentPoint = new Point(instance.currentPoint);

this.text = new StringBuilder(instance.text.toString());

}

@Override

public boolean canUndo() {

return historyStack.size() != 0;

}

@Override

public Object clone() {

try {

super.clone();

TextField instance = new TextField();

instance.currentPoint = new Point(currentPoint);

instance.text = new StringBuilder(text.toString());

instance.historyStack = new ArrayDeque<>(historyStack);

instance.futureStack = new ArrayDeque<>(futureStack);

return instance;

} catch (CloneNotSupportedException e) {

e.printStackTrace();

}

return null;

}

//打印输出

public void reportMyself() {

System.out.printf("Pos=(%d, %d) Text=\"%s\"%n", currentPoint.x, currentPoint.y, text.toString());

}

}

我们通过两个Queue来对状态进行储存,当调用undo或redo的时候,从相应的Queue中获得状态,然后覆盖给当前的对象。这样一来,我们就实现了备忘录模式。

注意其中用到了上篇文章中说到的原型模式(Prototype),由于我们必须进行深拷贝,所以实现了Cloneable接口,新建了一块内存保存对象的状态。

其实我们也可以不保存TextField对象,而是针对其中的关键数据,如Point的x/y坐标,StringBuilder对应的String进行保存,这样能够更节省内存。

SVN

其实我们日常使用的SVN,也可以算作是一种备忘录模式。它将不同版本的文件统一保存起来,当我们需要获得某一个时刻的文件版本时,可以从SVN库中拉取对应记录。

其实这有一点数据仓库的感觉,将每一版本的截面数据保存起来,用于后续的恢复和查看。而我们进行的文件保存与恢复,或者序列化与反序列化,如果按照备忘录模式的定义,也可以算作一种体现形式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值