23个java设计模式(二十二)-- 备忘录模式

2016年03月28日 原创
关键词: java 设计模式
摘要 备忘录模式在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态。

一、概述。

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

对象的状态依赖于它的变量的取值情况,对象在程序运行期间的各个时期可能具有不同的状态。在某些应用中,程序可能需要使用一种合理的方式来保存对象在某一时刻的状态,以便在需要时,对象能恢复到原先保存的状态。

备忘录模式是关于怎样保存对象状态的成熟模式,其关键是提供一个备忘录对象,该备忘录负责存储一个对象的状态,程序可以在磁盘或内存中保存这个备忘录,这样一来,程序就可以根据对象的备忘录将该对象恢复到备忘录中所存储的状态。

二、备忘录模式的结构。

备忘录模式包括三种角色:

  • 原发者(Originator):需要在某个时刻保存其状态的对象。原发者负责创建备忘录,然后原发者使用该备忘录记录自己的状态。当原发者需要恢复某个时刻的状态时,它通过获得相应备忘录中的数据来恢复那个时刻的状态。
  • 备忘录(Memento):负责存储原发者状态的对象,创建备忘录的类和创建原发者的类在同一个包中,该类提供的访问数据的方法都是友元方法,使得只有和原发者在同一个包中的类的实例才可以访问备忘录中的数据。
  • 负责人(Caretaker):负责管理保存备忘录的对象。负责人如果不和原发者在同一个包中,就不能对备忘录中的内容进行修改或读取。如果需要将备忘录保存到磁盘,负责人可以使用对象流将备忘录写入文件。

三、示例程序。

原发者(Originator)

ReadPhrase.java

public class ReadPhrase {
	private long readPosition;
	private File file;
	private RandomAccessFile raf;
	private String phrase;
	public ReadPhrase(File file) {
		this.file = file;
		try {
			raf = new RandomAccessFile(file, "r");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}
	public Memento createMemento() {
		Memento mem = new Memento();
		mem.setState(readPosition);
		return mem;
	}
	public void restoreFromMemento(Memento mem) {
		readPosition = mem.getState();
	}
	public String readLine() {
		try {
			raf.seek(readPosition);
			phrase = raf.readLine();
			if(phrase!=null) {
				byte[] bytes = phrase.getBytes("iso-8859-1");
				phrase = new String(bytes);
			}
			readPosition = raf.getFilePointer();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return phrase;
	}
	public void closeRead() {
		try {
			raf.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

备忘录(Memento)

Memento.java

public class Memento implements Serializable{
	private long state;

	long getState() {
		return state;
	}

	void setState(long state) {
		this.state = state;
	}
	
}

负责人(Caretaker)

Caretaker.java

public class Caretaker {
	private File file;
	private Memento memento;
	public Caretaker() {
		file = new File("saveObject.txt");
	}
	public Memento getMemento() {
		if(file.exists()) {
			try {
				FileInputStream fis = new FileInputStream("saveObject.txt");
				ObjectInputStream ois = new ObjectInputStream(fis);
				memento = (Memento) ois.readObject();
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return memento;
	}
	public void saveMemento(Memento memento) {
		try {
			FileOutputStream fos = new FileOutputStream("saveObject.txt");
			ObjectOutputStream oos = new ObjectOutputStream(fos);
			oos.writeObject(memento);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

测试程序

phrase.txt

安步当车
一心一意
背水一战

Example1Test.java

public class Example1Test {
	@Test
	public void testMain() {
		Scanner scanner = new Scanner(System.in);
		ReadPhrase readPhrase = new ReadPhrase(new File("phrase.txt"));
		File favorPhrase = new File("favorPhrase.txt");
		RandomAccessFile raf = null;
		try {
			raf = new RandomAccessFile(favorPhrase, "rw");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		System.out.println("是否从上次读取的位置继续读取成语(y/n)");
		String answer = scanner.nextLine();
		if(answer.startsWith("y")||answer.startsWith("Y")) {
			Caretaker caretaker = new Caretaker();
			Memento memento = caretaker.getMemento();
			if(memento!=null) 
				readPhrase.restoreFromMemento(memento);
		}
		String phrase = null;
		while((phrase=readPhrase.readLine())!=null) {
			System.out.println(phrase);
			System.out.println("是否将该成语保存到"+favorPhrase.getName());
			answer = scanner.nextLine();
			if(answer.startsWith("y")||answer.startsWith("Y")) {
				try {
					raf.seek(favorPhrase.length());
					byte[] bytes = phrase.getBytes();
					raf.write(bytes);
					raf.writeChar('\n');
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			System.out.println("是否继续读取成语?(y/n)");
			answer= scanner.nextLine();
			if(answer.startsWith("y")||answer.startsWith("Y")) continue;
			else {
				readPhrase.closeRead();Caretaker caretaker = new Caretaker();
				caretaker.saveMemento(readPhrase.createMemento());
				try {
					raf.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				System.exit(0);
			}
		}
		
	}
}

四、备忘录模式的优点。

  1. 备忘录模式使用备忘录可以把原发者的内部状态保存起来,使只有很”亲密的“对象可以访问备忘录中的数据。
  2. 备忘录模式强调了类设计单一责任原则,即将状态的刻画和保存分开。

五、适合使用备忘录模式的情景。

  1. 必须保存一个对象在某一个时刻的全部或部分状态,以便在需要时恢复该对象先前的状态。
  2. 一个对象不想通过提供属性方法让其他对象得到自己的内部状态。

注:如果备忘录需要存储大量的数据或非常频繁地创建备忘录,可能会导致非常大的存储开销。