備忘錄是一種行為模式,被儲存的物件不會被外部讀取,在不破壞封裝的前提下,獲取、儲存一個物件的內部狀態
又稱為備忘錄 (Memo)
如果喜歡讀更好看一點的網頁版本,可以到我新做的網站 DevTech Ascendancy Hub
需要儲存一個物件在某一個時刻的狀態 (並非執行步驟)
提供 rollback 操作
當一個物件不希望直接被讀取到其狀態 (private),就可 透過中間物件存取 (這個中間物件就是 Memo)
Memo 定義:在不破獲封裝的前提下,保存一個物件的狀態,好讓之後可以恢復該對象的狀態
Memo UML 角色關係
Memo 類角色 | 說明 |
---|---|
Originator(發起人) | 讓使用者操控 (可讓使用者直接接觸),同時包含 Memo 功能 |
Memo(備忘錄) | 跟 Originator 有相對應的 member,讓 使用者無法直接接觸 Memo 類 |
Caretaker(管理備忘錄) | 專門用來記錄、管理 Memo 類 |
Memo 有很清楚得分出類之間的職責,符合單一職責、迪米特(最少知識)原則
單一職責:
Originator
負責操作備忘錄、Memo
負責紀錄數據、Caretaker
管理紀錄迪米特:User 不會直接接觸到 Memo,使用者只會知道
Originator
優點 :
提供使用者快速方便的恢復機制,方便找回歷史狀態或是 rollback 資料,而這些操作都是安全性的操作
從上面可以看出 使用者不必關心細節 (getter
、setter
)
缺點 :
消耗資源,類的增加 (通病),每次儲存都會消耗資源空間
Originator
類:
GameProvider
讓使用者直接操控的類 (對外暴露),同時包括 Memo 類的創建、操作,提供給使用者使用
Memo
類:
專注於儲存 Originator
需要的 Member,該類不會讓使用者直接使用
Originator
是跟 Memo
類有相同的 member,所以它必須擁有該類的所有屬性 (可用 data class
),在這部分可以用另外一個抽象優化
可以選擇使用 Memo 變形、反射、抽象… 等等
Caretaker
類:
身為 Memo 管理員,這裏使用 Map 儲存 Memo,專職在 處理存取,該類會讓使用者使用
User 使用:
User 必須使用 Originator
發起備份、Caretaker
管理(儲存、取得)備份
這裡我們可以使用 java 的特性,讓需要紀錄的對象實做 Cloneable
界面,讓其轉為可 Clone 的對象;優點是可以更簡單的使用、加快性能
不符合單一職責? 原本的對象需要覆蓋拷貝的責任了!
的確如此,但這裡我們可以想做,把拷貝責任封裝到 Cloneable
界面,其實這就算是一個簡易的封裝
這種變形僅限於 簡單場合
雖然 clone 可以加快性能,但是我們還是要注意 深、淺拷貝問題,避免增加了邏輯複雜度,再次簡化後 UML 如下
Originator
類:概括了保存、數據複製的工作
User 使用變形 Memo 設計:用起來也相當簡化,基本上就算是 clone
模式的變化板而已
Activity 被系統回收前會透過,特殊方法來儲存、恢復資料
功能 | Activity 處理的方法 |
---|---|
暫存 | onSaveInstanceState |
恢復 | onRestoreInstanceState |
Memo 設計的概念分類如下
Memo 設計腳色 | Android 實做 | 說明 |
---|---|---|
Memo | Bundle | 設定要緩存、攜帶的資料 |
Originator | View、ViewGroup | 一般的 View 操作,不過 多了 Bundle 存取控制 |
Caretaker | Activity、Fragment | 儲存、恢復 Bundle 數據 |
從 Activity
#onSaveInstanceState
開始看,可以看到這裡 取得 Bundle 對象 (也就是 Memo),呼叫 PhoneWindow 並傳入 Bundle 物件
PhoneWindow
#saveHierarchyState
方法:目的是 恢復 View 的狀態,創建 SparseArray<Parcelable>
並以 id 作為 key、Parcelable 作為 value (儲存 View 的訊息)
SparseArray<T>
簡單來說:是一個以 Integer 為 Key 的 Map 資料格式,也就是 Map<Integer, T>
,不過它的效率較高saveHierarchyState
方法:View 遞迴呼叫,讓每個 View、ViewGroup 自己處理 (ViewGroup 會在 Override 這個函數)
View#dispatchSaveInstanceState
方法:
從這邊可以看出 xml 上 沒有設定 android:id
屬性的 View 是不會回復狀態 !
由於儲存 View 的空間是使用 SparseArray 結構,所以 同一個 View Tree 上不能有相同的 ID 的 View,否則就只會有一個更新
ViewGroup#dispatchRestoreInstanceState
方法:迭代該 ViewGroup 中所有的 View,並遞迴呼叫所有 View#dispatchRestoreInstanceState
方法來回復 View 的狀態
其實流程跟 onSaveInstanceState
差不多,只是做了反向操作
從 Activity
#onRestoreInstanceState
開始看,可以看到這裡 取得 Bundle 對象 (也就是 Memo),呼叫 PhoneWindow
並傳入 Bundle 物件
PhoneWindow#restoreHierarchyState
方法:目的是 恢復 View 的狀態,取得 SparseArray<Parcelable>
並以 id 作為 key、Parcelable 作為 value (儲存 View 的訊息)
SparseArray<T>
簡單來說:是一個以 Integer 為 Key 的 Map 資料格式,也就是 Map<Integer, T>
,不過它的效率較高restoreHierarchyState
方法:View 遞迴呼叫,讓每個 View、ViewGroup 自己處理 (ViewGroup 會在 Override 這個函數)
View
#dispatchRestoreInstanceState
方法:
從這邊可以看出 xml 上 沒有設定 android:id
屬性的 View 是不會回復狀態 !
由於儲存 View 的空間是使用 SparseArray 結構,所以 同一個 View Tree 上不能有相同的 ID 的 View,否則就只會有一個更新
ViewGroup
#dispatchRestoreInstanceState
方法:迭代該 ViewGroup 中所有的 View,並遞迴呼叫所有 View#dispatchRestoreInstanceState
方法來回復 View 的狀態
物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)!
創建模式 - Creation Patterns
:
創建模式用於「物件的創建」,它關注於如何更靈活、更有效地創建對象。這些模式可以隱藏創建對象的細節,並提供創建對象的機制,例如單例模式、工廠模式… 等等,詳細解說請點擊以下連結
行為模式 - Behavioral Patterns
:
行為模式關注物件之間的「通信」和「職責分配」。它們描述了一系列對象如何協作,以完成特定任務。這些模式專注於改進物件之間的通信,從而提高系統的靈活性。例如,策略模式、觀察者模式… 等等,詳細解說請點擊以下連結
結構模式 - Structural Patterns
:
結構模式專注於「物件之間的組成」,以形成更大的結構。這些模式可以幫助你確保當系統進行擴展或修改時,不會破壞其整體結構。例如,外觀模式、代理模式… 等等,詳細解說請點擊以下連結
Java 設計模式
基礎進階