備忘錄模式是使用一個(gè)備忘錄對(duì)象把另外一個(gè)對(duì)象內(nèi)部狀態(tài)進(jìn)行保存,在適當(dāng)?shù)臅r(shí)候還原到某個(gè)狀態(tài)。如同我們記錄某件事件,在需要回憶的時(shí)候去看下記事本。
先來(lái)看下類圖

該模式涉及到3個(gè)角色:
- 發(fā)起人角色:
Originator,該角色包含備忘錄對(duì)象,備忘錄對(duì)象存儲(chǔ)了他的狀態(tài); - 負(fù)責(zé)人角色:
Caretaker,該角色保存?zhèn)渫泴?duì)象,但不檢查備忘錄對(duì)象內(nèi)容; - 備忘錄角色:
Memento,將發(fā)起人對(duì)象的狀態(tài)保存起來(lái),,保護(hù)發(fā)起人的內(nèi)容不被外界訪問(wèn)
寬接口與白箱
備忘錄角色對(duì)如何其他對(duì)象提供一個(gè)接口,也就是寬接口的話,那么備忘錄角色存儲(chǔ)的內(nèi)部狀態(tài)都暴露給其他對(duì)象。這種情況導(dǎo)致發(fā)起人的狀態(tài)都沒(méi)看到,是破壞封裝性的,只能通過(guò)程序猿的自律。先來(lái)看下寬接口。

接下來(lái)看下代碼實(shí)現(xiàn):
public class Memento {
private String state;
public Memento(String state) {
this.state=state;
}
public String getState() {
return this.state;
}
public void setState(String state){
this.state=state;
}
}
上面這個(gè)備忘錄對(duì)象提供:1、對(duì)發(fā)起人的狀態(tài)進(jìn)行保存;2、可以訪問(wèn)的狀態(tài)是公開的;
//發(fā)起人
public class Originator {
private String state;
//創(chuàng)建備忘錄對(duì)象來(lái)保存狀態(tài)
public Memento createMemento(){
return new Memento(state);
}
//從備忘錄對(duì)象里面恢復(fù)狀態(tài)
public void restoreMemento(Memento m){
this.state=m.getState();
}
public String getState() {
return state;
}
public void setState(String state) {
System.out.println("當(dāng)前狀態(tài):"+state);
this.state = state;
}
}
發(fā)起人對(duì)象利用創(chuàng)建一個(gè)新的備忘錄對(duì)象保存自己的狀態(tài);
//負(fù)責(zé)人
public class Caretaker {
private Memento memento;
public Memento retrieveMemento(){
return this.memento;
}
public void saveMemento(Memento m){
this.memento=m;
}
}
負(fù)責(zé)人只負(fù)責(zé)保存?zhèn)渫泴?duì)象,對(duì)備忘錄對(duì)象里面的內(nèi)容不再變化??蛻舳说牟僮魅缦?/p>
public class Client {
public static void main(String[] args) {
Originator o=new Originator();
Caretaker c=new Caretaker();
//發(fā)起人狀態(tài)改變
o.setState("Start");
//負(fù)責(zé)人保存這個(gè)備忘錄
c.saveMemento(o.createMemento());
//改變狀態(tài)
o.setState("End");
//回到初始
o.restoreMemento(c.retrieveMemento());
}
}
/** ---- result ----
當(dāng)前狀態(tài):Start
當(dāng)前狀態(tài):End
*/
上面就是白箱操作,很簡(jiǎn)單,但是破壞了對(duì)發(fā)起人的狀態(tài)封裝;
雙重接口
所謂雙重接口就是對(duì)某一個(gè)對(duì)象提供寬接口,對(duì)另外一些類提供窄接口。系統(tǒng)中可能需要將某個(gè)對(duì)象的狀態(tài)保存起來(lái),在某個(gè)時(shí)候進(jìn)行恢復(fù),但這些狀態(tài)并不希望被外界訪問(wèn),以免有外界直接修改狀態(tài)的危險(xiǎn),這個(gè)時(shí)候,備忘錄模式就很好的解決這個(gè)問(wèn)題,他利用寬接口和窄接口來(lái)保證。
假設(shè)窄接口對(duì)所有類公開,而公開類只對(duì)某一個(gè)公開,這個(gè)時(shí)候,我們可以把實(shí)現(xiàn)了寬接口和窄接口的具體類作為這個(gè)特殊類的內(nèi)部類,寬接口的方法也可以移植到這個(gè)特殊類上,而具體類里面的方法都是私有,這樣對(duì)特殊類可以訪問(wèn)所有接口,其他類智能調(diào)用特殊類的某些公開方法。有點(diǎn)饒人,畫個(gè)圖

圖1是一個(gè)基本樣子,進(jìn)行演變,首先寬接口方法歸屬到具體類里面,變成下面這個(gè)樣子

這樣寬接口的方法還是公開的,此時(shí)把具體類作為特殊類的內(nèi)部類,并且,把里面的方法都設(shè)置私有

這個(gè)時(shí)候的特殊類,也就發(fā)起者代碼如下:
public class DoubleInterfaceDemo {
public static void main(String[] args) {
DoubleInterfaceDemo demo=new DoubleInterfaceDemo();
demo.new ConcreteCLass().wide();
demo.new ConcreteCLass().getConcrete().does();
}
class ConcreteCLass implements Narrow{
@Override
public void does() {
System.out.println("窄接口");
}
//這個(gè)接口只能DoubleInterfaceDemo自己用了
private void wide(){
System.out.println("寬接口");
}
public Narrow getConcrete(){
return (Narrow)new ConcreteCLass();
}
}
}
接下來(lái)看下黑箱的備忘錄模式,有了上面的介紹,那我們把備忘錄的具體實(shí)現(xiàn)作為內(nèi)部類放到發(fā)起人對(duì)象里面
下面看下具體代碼
//發(fā)起人 加強(qiáng)版 黑箱
public class Originator2 {
private String state;
public Originator2() {
}
public MementoIF createMemento(){
return new Memento2(this.state);
}
//內(nèi)部類 備忘錄
protected class Memento2 implements MementoIF{
private String saveState;
public Memento2(String saveState) {
this.saveState=saveState;
}
public String getSaveState() {
return saveState;
}
public void setSaveState(String saveState) {
this.saveState = saveState;
}
}
//從備忘錄對(duì)象里面恢復(fù)狀態(tài)
public void restoreMemento(MementoIF m){
this.state=((Memento2)m).getSaveState();
}
public String getState() {
return state;
}
public void setState(String state) {
System.out.println("當(dāng)前狀態(tài):"+state);
this.state = state;
}
}
負(fù)責(zé)人針對(duì)接口編程,代碼如下
//負(fù)責(zé)人
public class Caretaker2 {
private MementoIF memento;
public MementoIF retrieveMemento(){
return this.memento;
}
public void saveMemento(MementoIF m){
this.memento=m;
}
}
MementoIF這個(gè)接口是窄接口,里面可以有公共使用的方法,這里假設(shè)沒(méi)有方法。
最后測(cè)試下這個(gè)代碼
public class Client2 {
public static void main(String[] args) {
Originator2 o=new Originator2();
Caretaker2 c=new Caretaker2();
//發(fā)起人狀態(tài)改變
o.setState("Start");
//負(fù)責(zé)人保存這個(gè)備忘錄
c.saveMemento(o.createMemento());
//改變狀態(tài)
o.setState("End");
//回到初始
o.restoreMemento(c.retrieveMemento());
}
}
/**
最后結(jié)論和之前的一樣,但在設(shè)計(jì)上面已經(jīng)很不同
*/
有時(shí)候發(fā)起人內(nèi)部信息需要保存在別的地方,但是讀取還是發(fā)起人自己,此時(shí)備忘錄模式就可以把發(fā)起人信息對(duì)外封閉起來(lái)。