備忘錄模式是使用一個備忘錄對象把另外一個對象內部狀態(tài)進行保存旧巾,在適當?shù)臅r候還原到某個狀態(tài)耸序。如同我們記錄某件事件,在需要回憶的時候去看下記事本鲁猩。
先來看下類圖
該模式涉及到3個角色:
- 發(fā)起人角色:
Originator
坎怪,該角色包含備忘錄對象,備忘錄對象存儲了他的狀態(tài)廓握; - 負責人角色:
Caretaker
搅窿,該角色保存?zhèn)渫泴ο螅粰z查備忘錄對象內容隙券; - 備忘錄角色:
Memento
男应,將發(fā)起人對象的狀態(tài)保存起來,娱仔,保護發(fā)起人的內容不被外界訪問
寬接口與白箱
備忘錄角色對如何其他對象提供一個接口沐飘,也就是寬接口的話,那么備忘錄角色存儲的內部狀態(tài)都暴露給其他對象牲迫。這種情況導致發(fā)起人的狀態(tài)都沒看到耐朴,是破壞封裝性的,只能通過程序猿的自律盹憎。先來看下寬接口筛峭。
接下來看下代碼實現(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;
}
}
上面這個備忘錄對象提供:1、對發(fā)起人的狀態(tài)進行保存陪每;2蜒滩、可以訪問的狀態(tài)是公開的;
//發(fā)起人
public class Originator {
private String state;
//創(chuàng)建備忘錄對象來保存狀態(tài)
public Memento createMemento(){
return new Memento(state);
}
//從備忘錄對象里面恢復狀態(tài)
public void restoreMemento(Memento m){
this.state=m.getState();
}
public String getState() {
return state;
}
public void setState(String state) {
System.out.println("當前狀態(tài):"+state);
this.state = state;
}
}
發(fā)起人對象利用創(chuàng)建一個新的備忘錄對象保存自己的狀態(tài)奶稠;
//負責人
public class Caretaker {
private Memento memento;
public Memento retrieveMemento(){
return this.memento;
}
public void saveMemento(Memento m){
this.memento=m;
}
}
負責人只負責保存?zhèn)渫泴ο螅瑢渫泴ο罄锩娴膬热莶辉僮兓癖椤锌订?蛻舳说牟僮魅缦?/p>
public class Client {
public static void main(String[] args) {
Originator o=new Originator();
Caretaker c=new Caretaker();
//發(fā)起人狀態(tài)改變
o.setState("Start");
//負責人保存這個備忘錄
c.saveMemento(o.createMemento());
//改變狀態(tài)
o.setState("End");
//回到初始
o.restoreMemento(c.retrieveMemento());
}
}
/** ---- result ----
當前狀態(tài):Start
當前狀態(tài):End
*/
上面就是白箱操作,很簡單画株,但是破壞了對發(fā)起人的狀態(tài)封裝辆飘;
雙重接口
所謂雙重接口就是對某一個對象提供寬接口,對另外一些類提供窄接口谓传。系統(tǒng)中可能需要將某個對象的狀態(tài)保存起來蜈项,在某個時候進行恢復,但這些狀態(tài)并不希望被外界訪問续挟,以免有外界直接修改狀態(tài)的危險紧卒,這個時候,備忘錄模式就很好的解決這個問題诗祸,他利用寬接口和窄接口來保證跑芳。
假設窄接口對所有類公開轴总,而公開類只對某一個公開,這個時候博个,我們可以把實現(xiàn)了寬接口和窄接口的具體類作為這個特殊類的內部類怀樟,寬接口的方法也可以移植到這個特殊類上,而具體類里面的方法都是私有盆佣,這樣對特殊類可以訪問所有接口往堡,其他類智能調用特殊類的某些公開方法仲吏。有點饒人忿等,畫個圖
圖1是一個基本樣子炒辉,進行演變筹我,首先寬接口方法歸屬到具體類里面池磁,變成下面這個樣子
這樣寬接口的方法還是公開的测秸,此時把具體類作為特殊類的內部類腿椎,并且优烧,把里面的方法都設置私有
這個時候的特殊類佃蚜,也就發(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("窄接口");
}
//這個接口只能DoubleInterfaceDemo自己用了
private void wide(){
System.out.println("寬接口");
}
public Narrow getConcrete(){
return (Narrow)new ConcreteCLass();
}
}
}
接下來看下黑箱的備忘錄模式庸娱,有了上面的介紹,那我們把備忘錄的具體實現(xiàn)作為內部類放到發(fā)起人對象里面
下面看下具體代碼
//發(fā)起人 加強版 黑箱
public class Originator2 {
private String state;
public Originator2() {
}
public MementoIF createMemento(){
return new Memento2(this.state);
}
//內部類 備忘錄
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;
}
}
//從備忘錄對象里面恢復狀態(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("當前狀態(tài):"+state);
this.state = state;
}
}
負責人針對接口編程谐算,代碼如下
//負責人
public class Caretaker2 {
private MementoIF memento;
public MementoIF retrieveMemento(){
return this.memento;
}
public void saveMemento(MementoIF m){
this.memento=m;
}
}
MementoIF
這個接口是窄接口熟尉,里面可以有公共使用的方法,這里假設沒有方法洲脂。
最后測試下這個代碼
public class Client2 {
public static void main(String[] args) {
Originator2 o=new Originator2();
Caretaker2 c=new Caretaker2();
//發(fā)起人狀態(tài)改變
o.setState("Start");
//負責人保存這個備忘錄
c.saveMemento(o.createMemento());
//改變狀態(tài)
o.setState("End");
//回到初始
o.restoreMemento(c.retrieveMemento());
}
}
/**
最后結論和之前的一樣斤儿,但在設計上面已經(jīng)很不同
*/
有時候發(fā)起人內部信息需要保存在別的地方,但是讀取還是發(fā)起人自己恐锦,此時備忘錄模式就可以把發(fā)起人信息對外封閉起來往果。