存档读档的Memento——备忘录模式

在玩游戏的时候我们总要存档、读档,
怎样实现这个功能呢?

下面是一个简单的例子:
一个Role类,用于存储角色信息

  1. package memento. old;
  2.  
  3. public class Role {
  4.        
  5.         private String name;
  6.        
  7.         private int hp;
  8.        
  9.         private int mp;
  10.        
  11.         public Role ( ) {
  12.         }
  13.        
  14.         public Role ( String name, int hp, int mp ) {
  15.                 this. name = name;
  16.                 this. hp = hp;
  17.                 this. mp = mp;
  18.         }
  19.  
  20.         public int getHp ( ) {
  21.                 return hp;
  22.         }
  23.        
  24.         public void fight ( ) {
  25.                 this. hp = 0;
  26.                 this. mp = 0;
  27.         }
  28.  
  29.         public void setHp ( int hp ) {
  30.                 this. hp = hp;
  31.         }
  32.  
  33.         public int getMp ( ) {
  34.                 return mp;
  35.         }
  36.  
  37.         public void setMp ( int mp ) {
  38.                 this. mp = mp;
  39.         }
  40.  
  41.         public String getName ( ) {
  42.                 return name;
  43.         }
  44.  
  45.         public void setName ( String name ) {
  46.                 this. name = name;
  47.         }
  48.        
  49. }

 而这个TestCase表现了它是如何实现存档读档的:

  1. package memento. old;
  2.  
  3. import junit.framework.TestCase;
  4.  
  5. public class TestGame extends TestCase {
  6.        
  7.         public void testGame ( ) {
  8.                 Role role = new Role ( "YOYO", 100, 10 );
  9.                
  10.                 assertEquals (role. getHp ( ), 100 );
  11.                 assertEquals (role. getMp ( ), 10 );
  12.                
  13.                 //      新建角色对象用于备份
  14.                 Role bakRole = new Role ( );
  15.                 bakRole. setName (role. getName ( ) );
  16.                 bakRole. setHp (role. getHp ( ) );
  17.                 bakRole. setMp (role. getMp ( ) );
  18.                
  19.                 assertEquals (bakRole. getHp ( ), 100 );
  20.                 assertEquals (bakRole. getMp ( ), 10 );
  21.                
  22.                 //      角色战斗
  23.                 role. fight ( );
  24.                 assertEquals (role. getHp ( ), 0 );
  25.                 assertEquals (role. getMp ( ), 0 );
  26.                
  27.                 //      还原角色状态
  28.                 role. setHp (bakRole. getHp ( ) );
  29.                 role. setMp (bakRole. getMp ( ) );
  30.                
  31.                 assertEquals (role. getHp ( ), 100 );
  32.                 assertEquals (role. getMp ( ), 10 );
  33.         }
  34.  
  35. }

 

这个版本中布满了set方法!
这样也许你可以直接从外部修改你的角色信息,很是危险,
正好我们已经学习了原型模式,就用clone来实现它吧?
(这里的数据结构比较简单,因此我们使用浅复制就可以了)

Role类,这回实现了一个Cloneable接口:

  1. package memento. clone;
  2.  
  3. public class Role implements Cloneable {
  4.        
  5.         private String name;
  6.        
  7.         private int hp;
  8.        
  9.         private int mp;
  10.        
  11.         public Role ( ) {
  12.         }
  13.        
  14.         public Role ( String name, int hp, int mp ) {
  15.                 this. name = name;
  16.                 this. hp = hp;
  17.                 this. mp = mp;
  18.         }
  19.        
  20.         public int getHp ( ) {
  21.                 return hp;
  22.         }
  23.  
  24.         public int getMp ( ) {
  25.                 return mp;
  26.         }
  27.  
  28.         public String getName ( ) {
  29.                 return name;
  30.         }
  31.        
  32.         public void fight ( ) {
  33.                 this. hp = 0;
  34.                 this. mp = 0;
  35.         }
  36.  
  37.         @Override
  38.         protected Object clone ( ) {
  39.                 Role role = null;
  40.                 try {
  41.                         role = (Role ) super. clone ( );
  42.                 } catch ( CloneNotSupportedException e ) {
  43.                         e. printStackTrace ( );
  44.                 }
  45.                 return role;
  46.         }
  47.        
  48. }

 

[TestGame]。而备份时只需要克隆它的副本就行了:

  1. package memento. clone;
  2.  
  3. import junit.framework.TestCase;
  4.  
  5. public class TestGame extends TestCase {
  6.        
  7.         public void testGame ( ) {
  8.                 Role role = new Role ( "YOYO", 100, 10 );
  9.                
  10.                 assertEquals (role. getHp ( ), 100 );
  11.                 assertEquals (role. getMp ( ), 10 );
  12.                
  13.                 //      新建角色对象用于备份
  14.                 Role bakRole = (Role ) role. clone ( );
  15.                
  16.                 assertEquals (bakRole. getHp ( ), 100 );
  17.                 assertEquals (bakRole. getMp ( ), 10 );
  18.                
  19.                 //      角色战斗
  20.                 role. fight ( );
  21.                 assertEquals (role. getHp ( ), 0 );
  22.                 assertEquals (role. getMp ( ), 0 );
  23.                
  24.                 //      还原角色状态
  25.                 role = (Role ) bakRole. clone ( );
  26.                
  27.                 assertEquals (role. getHp ( ), 100 );
  28.                 assertEquals (role. getMp ( ), 10 );
  29.         }
  30.  
  31. }

 

额,还是有点问题,
我的角色信息里不想保存角色姓名,因为它根本不会改变!
这里只是一个姓名还好,但如果有很多不需要存储的数据,使用原型模式就不大合适了。

因此,我们引入这次要讲的备忘录模式吧。
首先还是Role类,它增加了一个存档的createMemento方法和一个读档的restoreMemento方法。

  1. package memento. normal;
  2.  
  3. public class Role {
  4.        
  5.         private String name;
  6.        
  7.         private int hp;
  8.        
  9.         private int mp;
  10.        
  11.         public Role ( ) {
  12.         }
  13.        
  14.         public Role ( String name, int hp, int mp ) {
  15.                 this. name = name;
  16.                 this. hp = hp;
  17.                 this. mp = mp;
  18.         }
  19.        
  20.         public void fight ( ) {
  21.                 this. hp = 0;
  22.                 this. mp = 0;
  23.         }
  24.  
  25.         public int getHp ( ) {
  26.                 return hp;
  27.         }
  28.  
  29.         public int getMp ( ) {
  30.                 return mp;
  31.         }
  32.  
  33.         public String getName ( ) {
  34.                 return name;
  35.         }
  36.  
  37.         public RoleMemento createMemento ( ) {
  38.                 return new RoleMemento (name, hp, mp );
  39.         }
  40.  
  41.         public void restoreMemento (RoleMemento memento ) {
  42.                 name = memento. getName ( );
  43.                 hp = memento. getHp ( );
  44.                 mp = memento. getMp ( );
  45.         }
  46.        
  47. }

 

接着则是RoleMemento,角色状态存储箱,它存储了我们要保存的状态。

  1. package memento. normal;
  2.  
  3. public class RoleMemento  {
  4.        
  5.         private String name;
  6.        
  7.         private int hp;
  8.        
  9.         private int mp;
  10.        
  11.         public RoleMemento ( String name, int hp, int mp ) {
  12.                 this. name = name;
  13.                 this. hp = hp;
  14.                 this. mp = mp;
  15.         }
  16.  
  17.         public int getHp ( ) {
  18.                 return hp;
  19.         }
  20.  
  21.         public int getMp ( ) {
  22.                 return mp;
  23.         }
  24.  
  25.         public String getName ( ) {
  26.                 return name;
  27.         }
  28.  
  29. }

 

除此之外,我们还需要一个Caretaker类,用来管理存档:

  1. package memento. normal;
  2.  
  3. public class Caretaker {
  4.        
  5.         private RoleMemento memento;
  6.        
  7.         public void saveMemento (RoleMemento memento ) {
  8.                 this. memento = memento;
  9.         }
  10.        
  11.         public RoleMemento retrieveMemento ( ) {
  12.                 return memento;
  13.         }
  14.  
  15. }

 

再看看我们的TestGame,它由一个Caretaker来对指定角色进行存档和读档。

  1. package memento. normal;
  2.  
  3. import junit.framework.TestCase;
  4.  
  5. public class TestGame extends TestCase {
  6.        
  7.         public void testGame ( ) {
  8.                 Role role = new Role ( "YOYO", 100, 10 );
  9.                
  10.                 assertEquals (role. getHp ( ), 100 );
  11.                 assertEquals (role. getMp ( ), 10 );
  12.                
  13.                 //      新建备份
  14.                 Caretaker bak = new Caretaker ( );
  15.                 bak. saveMemento (role. createMemento ( ) );
  16.                
  17.                 //      角色战斗
  18.                 role. fight ( );
  19.                 assertEquals (role. getHp ( ), 0 );
  20.                 assertEquals (role. getMp ( ), 0 );
  21.                
  22.                 //      还原状态
  23.                 role. restoreMemento (bak. retrieveMemento ( ) );
  24.                
  25.                 assertEquals (role. getHp ( ), 100 );
  26.                 assertEquals (role. getMp ( ), 10 );
  27.         }
  28.  
  29. }

 

这就是一个简单的备忘录模式的实现了。
数据不会被外部随意访问。

备忘录模式(Memento)

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

适用性:

  • 必须保存一个对象在某一个时刻的(部分)状态, 这样以后需要时它才能恢复到先前的状态。
  • 如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。

提到备忘录模式,就不得不提到宽接口和窄接口。

在这个例子中我们可以看到,
Role类可以访问Memento内部的数据,Memento对Role提供了一个宽接口,
而对于Caretaker,它只是实现了管理Memento的作用,但并不访问Memento内部的数据,即Memento对Caretaker提供的是窄接口。

保护其内容不被发起人(Originator)对象之外的任何对象所读取。提供了两个等效的接口:宽接口和窄接口。
我们说说宽接口和窄接口的定义:

     窄接口:负责人(Caretaker)对象(和其他除发起人对象之外的任何对象)看到的是备忘录的窄接口(narrow interface),这个窄接口只允许它把备忘录对象传给其他的对象。
     宽接口:发起人(Originator)对象可以看到一个宽接口(wide interface),这个宽接口允许它读取所有的数据,以便根据这些数据恢复发起人对象的内部状态。

这里用的例子是一个“白箱”的实现,对于“黑箱”实现及“java实现双重接口”参见《java与模式》。

* 一个Caretaker是可以保存多个Memento的,本例为了简单所以只设计了一个。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值