标签 : Java与设计模式
备忘录模式: 在不破坏封装性的前提下, 捕获一个对象的内部状态( or 拷贝), 并在该对象之外保存这个状态, 这样以后就可 将该对象恢复到原先保存的状态.
(图片来源: 设计模式: 可复用面向对象软件的基础)
将保存细节封装在Memento中, 后面即使修改了保存细节也不会影响客户端.
案例: 游戏进度保存
在攻击Boss前, 将当前游戏进度保存, 万一失败还可从保存点重新开始.
/**
* 游戏角色, 原发器
*
* @author jifang
* @since 16/8/29 上午10:05.
*/
public class GameRoleOriginator {
private int vit; // 生命值
private int atk; // 攻击力
private int def; // 防御力
public GameRoleOriginator(int vit, int atk, int def) {
this.vit = vit;
this.atk = atk;
this.def = def;
}
public void fight() {
vit -= 10;
atk -= 8;
def += 10;
}
public RoleStateMemento save() {
return new RoleStateMemento(vit, atk, def);
}
public void recover(RoleStateMemento memento) {
this.setVit(memento.getVit());
this.setAtk(memento.getAtk());
this.setDef(memento.getDef());
}
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
@Override
public String toString() {
return "GameRoleOriginator{" +
"vit=" + vit +
", atk=" + atk +
", def=" + def +
'}';
}
}
friend
提供支持). 理想的情况是只允许生成本备忘录的那个原发器访问本备忘录的内部状态.public class RoleStateMemento {
private int vit;
private int atk;
private int def;
public RoleStateMemento(int vit, int atk, int def) {
this.vit = vit;
this.atk = atk;
this.def = def;
}
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
}
public class RoleStateCaretaker {
private Deque stack = new LinkedList<>();
public void save(RoleStateMemento memento) {
stack.push(memento);
}
public RoleStateMemento checkout() {
return stack.pop();
}
}
public class Client {
@Test
public void client() {
RoleStateCaretaker caretaker = new RoleStateCaretaker();
GameRoleOriginator originator = new GameRoleOriginator(100, 50, 50);
System.out.println("角色初始状态: " + originator);
// 保存进度
caretaker.save(originator.save());
System.out.println("fight boss...");
originator.fight();
System.out.println("阻击Boss后的状态: " + originator);
originator.recover(caretaker.checkout());
System.out.println("恢复后的状态: " + originator);
}
}
如果Memento需要保存的是Originator的所有属性, 那么可将Originator的所有属性都存储到一个
Map
结构中由Caretaker保存, 这样就节省了Memento中间类的开发成本. 甚至还可将Originator序列化为二进制流/字符串存储到持久化设备中(如磁盘、DB、Redis), 节省内存开销, 下面演示将Originator转化为Map
存储:
save()
/recover()
的变化:public class GameRoleOriginator {
private int vit; // 生命值
private int atk; // 攻击力
private int def; // 防御力
// ...
public void fight() {
vit -= 10;
atk -= 8;
def += 10;
}
public Map save() {
try {
return BeanUtil.bean2Map(this);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public void recover(Map memento) {
GameRoleOriginator bean;
try {
bean = BeanUtil.map2Bean(memento);
} catch (IllegalAccessException | InstantiationException | NoSuchFieldException e) {
throw new RuntimeException(e);
}
this.setVit(bean.getVit());
this.setAtk(bean.getAtk());
this.setDef(bean.getDef());
}
// ...
}
public class BeanUtil {
public static Map bean2Map(Object object) throws IllegalAccessException {
Map map = new HashMap<>();
Class> clazz = object.getClass();
map.put("class", clazz);
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
String key = field.getName();
Object value = field.get(object);
map.put(key, value);
}
return map;
}
public static T map2Bean(Map map) throws IllegalAccessException, InstantiationException, NoSuchFieldException {
Class> clazz = (Class>) map.get("class");
Field[] fields = clazz.getDeclaredFields();
Object object = clazz.newInstance();
for (Field field : fields) {
field.setAccessible(true);
Object value = map.get(field.getName());
field.set(object, value);
}
return (T) object;
}
}
适用性
相关模式