設(shè)計(jì)模式備忘錄模式

1.備忘錄模式的定義及使用場(chǎng)景
備忘錄模式是一種行為模式,該模式用于保存對(duì)象當(dāng)前狀態(tài),并且在之后可以再次恢復(fù)到此狀態(tài)。備忘錄模式實(shí)現(xiàn)的方式需要保證被保存的對(duì)象狀態(tài)不能被對(duì)象從外部訪問(wèn),目的是為了保護(hù)好被保存的這些對(duì)象狀態(tài)的完整性以及內(nèi)部實(shí)現(xiàn)不向外暴露。
定義:
在不破壞封閉的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存這個(gè)狀態(tài),這樣,以后就可將該對(duì)象回復(fù)到原先保存的狀態(tài)
使用場(chǎng)景:

需要保存一個(gè)對(duì)象在某一個(gè)時(shí)刻的狀態(tài)或部分狀態(tài)
需要用一個(gè)接口來(lái)讓其他對(duì)象得到這些狀態(tài),將會(huì)暴露對(duì)象的實(shí)現(xiàn)細(xì)節(jié)并破壞對(duì)象的封裝性,一個(gè)對(duì)象不希望外界直接訪問(wèn)其內(nèi)部狀態(tài),通過(guò)中間對(duì)象可以間接訪問(wèn)其內(nèi)部狀態(tài)

Paste_Image.png

2. 備忘錄模式的優(yōu)缺點(diǎn)
2.1優(yōu)點(diǎn)
給用戶提供了一種可以恢復(fù)狀態(tài)的機(jī)制,可以使用戶能夠比較方便地回到某個(gè)歷史的狀態(tài)
實(shí)現(xiàn)了信息的封裝,使得用戶不需要關(guān)心狀態(tài)的保持細(xì)節(jié)
2.2缺點(diǎn)
消耗資源,如果類的成員變量過(guò)多,勢(shì)必會(huì)占用比較大的資源,而且每一次保存都會(huì)消耗一定的內(nèi)存

3.注意事項(xiàng)

備忘錄的生命期備忘錄創(chuàng)建出來(lái)就要在“最近”的代碼中使用,要主動(dòng)管理它的生命周期,建立就要使用,不使用就要立刻刪除其引用,等待垃圾回收器對(duì)它的回收處理
備忘錄的性能不要在頻繁建立備份的場(chǎng)景中使用備忘錄模式(比如一個(gè)for循環(huán)中),主要原因是一是控制不了備忘錄建立的對(duì)象數(shù)量;而是大對(duì)象的建立是要消耗資源的,系統(tǒng)的性能需要考慮。
4. 備忘錄模式的實(shí)現(xiàn)方式

public class Memento {
    //發(fā)起人的內(nèi)部狀態(tài)
    private String state="";
    public Memento(String state) {
        this.state = state;
    }
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
}```

public class Caretaker {
//備忘錄對(duì)象
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}```

public class Originator {
    //內(nèi)部狀態(tài)
    private String state = "";
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
    //創(chuàng)建一個(gè)備忘錄
    public Memento createMemento() {
        return new Memento(state);
    }
    //恢復(fù)一個(gè)備忘錄
    public void restoreMemento(Memento memento) {
        this.setState(memento.getState());
    }
}```

public class Test {
public static void main(String args[]) {
//定義出發(fā)起人
Originator originator = new Originator();
originator.setState("初始:1111111111111");
//定義出備忘錄管理員
Caretaker caretaker = new Caretaker();
System.out.println(originator.getState());
//創(chuàng)建一個(gè)備忘錄
caretaker.setMemento(originator.createMemento());
originator.setState("改變:22222222222222");
System.out.println(originator.getState());
originator.setState("恢復(fù):restoreMemento");
originator.restoreMemento(caretaker.getMemento());
System.out.println(originator.getState());
}
}```
5. 備忘錄模式在Android中的實(shí)際應(yīng)用
在Android開(kāi)發(fā)中,狀態(tài)模式應(yīng)用是Android中的狀態(tài)保持,也就是里面的onSaveInstanceState和onRestoreInstanceState。當(dāng)Activity不是正常方式退出,且Activity在隨后的時(shí)間內(nèi)被系統(tǒng)殺死之前會(huì)調(diào)用這兩個(gè)方法讓開(kāi)發(fā)人員可以有機(jī)會(huì)存儲(chǔ)Activity的相關(guān)信息,并且在下次放好Activity的時(shí)候恢復(fù)這些數(shù)據(jù)。
Activity:

protected void onSaveInstanceState(Bundle outState) {
 outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState()); //保存當(dāng)前窗口的視圖樹(shù)的狀態(tài)
 Parcelable p = mFragments.saveAllState(); //存儲(chǔ)Fragment的狀態(tài)
 if (p != null) {
 outState.putParcelable(FRAGMENTS_TAG, p);
 }
 getApplication().dispatchActivitySaveInstanceState(this, outState);
 }```
Window的實(shí)現(xiàn)為PhoneWindow:

/** {@inheritDoc} */
@Override
public Bundle saveHierarchyState() {
Bundle outState = new Bundle();
if (mContentParent == null) {
return outState;
}
//通過(guò)SparseArray類來(lái)存儲(chǔ),這相當(dāng)于一個(gè)key為整型的map
SparseArray<Parcelable> states = new SparseArray<Parcelable>();
//mContentParent就是調(diào)用Activity的setContentView函數(shù)設(shè)置的內(nèi)容視圖,它是內(nèi)容視圖的根節(jié)點(diǎn),在這里存儲(chǔ)整棵樹(shù)的結(jié)構(gòu)
mContentParent.saveHierarchyState(states);
//將視圖樹(shù)結(jié)構(gòu)放到outState中
outState.putSparseParcelableArray(VIEWS_TAG, states);
// 保存當(dāng)前界面中獲取了焦點(diǎn)的View
View focusedView = mContentParent.findFocus();
if (focusedView != null) {
if (focusedView.getId() != View.NO_ID) {
outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
} else {
if (false) {
Log.d(TAG, "couldn't save which view has focus because the focused view "

  • focusedView + " has no id.");
    }
    }
    }
    // 存儲(chǔ)整個(gè)面板的狀態(tài)
    SparseArray<Parcelable> panelStates = new SparseArray<Parcelable>();
    savePanelState(panelStates);
    if (panelStates.size() > 0) {
    outState.putSparseParcelableArray(PANELS_TAG, panelStates);
    }
    //存儲(chǔ)actionBar的狀態(tài)
    if (mDecorContentParent != null) {
    SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>();
    mDecorContentParent.saveToolbarHierarchyState(actionBarStates);
    outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
    }
    return outState;
    }```
    在saveHierarchyState函數(shù)中,主要是存儲(chǔ)了與當(dāng)前UI、ActionBar相關(guān)的View狀態(tài)。mContentParent就是我們通過(guò)Activity的setContentView函數(shù)設(shè)置的內(nèi)容視圖,他是這個(gè)內(nèi)容視圖的根節(jié)點(diǎn)。mContentParent是一個(gè)ViewGroup對(duì)象,但是saveHierachyState是在父類View中
public void saveHierarchyState(SparseArray<Parcelable> container) {
 dispatchSaveInstanceState(container);
 }```

protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
Parcelable state = onSaveInstanceState();
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
throw new IllegalStateException(
"Derived class did not call super.onSaveInstanceState()");
}
if (state != null) {
// Log.i("View", "Freezing #" + Integer.toHexString(mID)
// + ": " + state);
container.put(mID, state); //將自身狀態(tài)放到container中 key 為id value為自身狀態(tài)
}
}
}```

//View類默認(rèn)的存儲(chǔ)的狀態(tài)為空
 protected Parcelable onSaveInstanceState() {
  mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
  if (mStartActivityRequestWho != null) {
  BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE);
  state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
  return state;
  }
  return BaseSavedState.EMPTY_STATE;
  }```

恢復(fù)數(shù)據(jù)的調(diào)用過(guò)程如下,基本流程與保存類似
Activity:

protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
mWindow.restoreHierarchyState(windowState);
}
}
}```
PhoneWindow:

/** {@inheritDoc} */
 @Override
 public void restoreHierarchyState(Bundle savedInstanceState) {
 if (mContentParent == null) {
 return;
 }
 SparseArray<Parcelable> savedStates
 = savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
 if (savedStates != null) {
 mContentParent.restoreHierarchyState(savedStates);
 }
 // restore the focused view
 int focusedViewId = savedInstanceState.getInt(FOCUSED_ID_TAG, View.NO_ID);
 if (focusedViewId != View.NO_ID) {
 View needsFocus = mContentParent.findViewById(focusedViewId);
 if (needsFocus != null) {
 needsFocus.requestFocus();
 } else {
 Log.w(TAG,
 "Previously focused view reported id " + focusedViewId
 + " during save, but can't be found during restore.");
 }
 }
 // restore the panels
 SparseArray<Parcelable> panelStates = savedInstanceState.getSparseParcelableArray(PANELS_TAG);
 if (panelStates != null) {
 restorePanelState(panelStates);
 }
 if (mDecorContentParent != null) {
 SparseArray<Parcelable> actionBarStates =
 savedInstanceState.getSparseParcelableArray(ACTION_BAR_TAG);
 if (actionBarStates != null) {
 doPendingInvalidatePanelMenu();
 mDecorContentParent.restoreToolbarHierarchyState(actionBarStates);
 } else {
 Log.w(TAG, "Missing saved instance states for action bar views! " +
 "State will not be restored.");
 }
 }
 }```
View:

public void restoreHierarchyState(SparseArray<Parcelable> container) {
dispatchRestoreInstanceState(container);
}```

/**
* Called by {@link #restoreHierarchyState(android.util.SparseArray)} to retrieve the
* state for this view and its children. May be overridden to modify how restoring
* happens to a view's children; for example, some views may want to not store state
* for their children.
*
* @param container The SparseArray which holds previously saved state.
*
* @see #dispatchSaveInstanceState(android.util.SparseArray)
* @see #restoreHierarchyState(android.util.SparseArray)
* @see #onRestoreInstanceState(android.os.Parcelable)
*/
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID) {
Parcelable state = container.get(mID);
if (state != null) {
// Log.i("View", "Restoreing #" + Integer.toHexString(mID)
// + ": " + state);
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
onRestoreInstanceState(state);
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
throw new IllegalStateException(
"Derived class did not call super.onRestoreInstanceState()");
}
}
}
}```

protected void onRestoreInstanceState(Parcelable state) {
mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
if (state != null && !(state instanceof AbsSavedState)) {
throw new IllegalArgumentException("Wrong state class, expecting View State but "

  • "received " + state.getClass().toString() + " instead. This usually happens "
  • "when two views of different type have the same id in the same hierarchy. "
  • "This view's id is " + ViewDebug.resolveId(mContext, getId()) + ". Make sure "
  • "other views do not use the same id.");
    }
    if (state != null && state instanceof BaseSavedState) {
    mStartActivityRequestWho = ((BaseSavedState) state).mStartActivityRequestWhoSaved;
    }
    }```
    在這個(gè)過(guò)程中,Activity扮演了Caretaker角色,負(fù)責(zé)存儲(chǔ)、恢復(fù)UI的狀態(tài)信息;Activity、Fragement、View、ViewGroup等對(duì)象為Originator角色,也就是需要存儲(chǔ)狀態(tài)的對(duì)象;Memento則是由Bundle類扮演。

Activity在停止之前會(huì)根據(jù)Activity的退出情景來(lái)選擇是否需要存儲(chǔ)狀態(tài),在重新啟動(dòng)該Activity時(shí)會(huì)判斷ActivityClientRecord對(duì)象中是否存儲(chǔ)了Activity的狀態(tài),如果含有狀態(tài)則調(diào)用Activity的onRestoreInstanceState函數(shù),從而使得Activity的UI效果與上次保持一致,這樣一來(lái),就保證了在非正常退出Activity時(shí)不會(huì)丟失數(shù)據(jù)的情況,很好地提升了用戶體驗(yàn)。
出處:http://huangjunbin.com/page/2/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 定義 備忘錄模式又叫做快照模式(Snapshot Pattern)或Token模式,是對(duì)象的行為模式。 備忘錄對(duì)象...
    步積閱讀 3,118評(píng)論 0 2
  • 1 場(chǎng)景問(wèn)題# 1.1 開(kāi)發(fā)仿真系統(tǒng)## 考慮這樣一個(gè)仿真應(yīng)用,功能是:模擬運(yùn)行針對(duì)某個(gè)具體問(wèn)題的多個(gè)解決方案,記...
    七寸知架構(gòu)閱讀 2,248評(píng)論 1 50
  • 介紹 備忘錄模式(Memento Pattern)保存一個(gè)對(duì)象的某個(gè)狀態(tài),以便在適當(dāng)?shù)臅r(shí)候恢復(fù)對(duì)象。備忘錄模式屬于...
    東西的南北閱讀 391評(píng)論 0 1
  • 備忘錄模式是使用一個(gè)備忘錄對(duì)象把另外一個(gè)對(duì)象內(nèi)部狀態(tài)進(jìn)行保存,在適當(dāng)?shù)臅r(shí)候還原到某個(gè)狀態(tài)。如同我們記錄某件事件,在...
    breezedancer閱讀 1,749評(píng)論 0 51
  • 7.17 #每日心得# 1.要提高工作效率 不能像學(xué)習(xí)時(shí)一樣邊學(xué)邊玩 要改掉多年的壞毛病 大部分的加班都是上班時(shí)間...
    七筒妹妹閱讀 251評(píng)論 0 0

友情鏈接更多精彩內(nèi)容