重構資料比重構函式更難——你無法只靠提取方法就把它簡化掉。封裝變數把「重組資料」這件難事,轉化成比較單純的「重組函式」:讓所有讀寫都走 getter/setter,於是你有了一個明確的落腳點,可以加驗證、監看變化,日後再從容改動資料本身。
🧠 Why It's a Problem
一個到處都能直接存取的資料,就像散在各處的許多入口——你想改它的結構、加限制,得逐一追殺每個使用點。封裝把這些入口收攏成兩個函式(讀與寫),於是你有了單一觀測點,能控制資料如何被存取與修改。物件導向鼓勵把物件資料保持私有,正是這個道理。
- 存取控制:你需要驗證或限制對資料的讀寫時,setter 就是唯一該加檢查的地方。
- 集中管理:把取值、更新值的地方收攏起來,日後維護只動一處。
- 不變式強制:在讀寫時觸發副作用或強制規則(如快取、通知)。
NOTE
真正不可變的資料不需要封裝——它無法被修改,就沒有程式碼會讀到過時值。不可變性本身就是一種強大的保障。
⚖️ Refactoring
機械步驟:① 建立讀取與寫入的封裝函式(getter/setter)→ ② 把每個直接引用改成呼叫對應函式 → ③ 測試 → ④ 收緊原變數的可見範圍(改為私有)。
前 — 裸露的資料,人人可直接讀寫
let configValue = 50;
console.log(configValue);
configValue = 100;configValue 是個誰都能直接動的變數。想加驗證、或想觀察它何時被改,你沒有任何一個地方可以下手。
後 — 讀寫都走封裝函式
class Configuration {
constructor() {
this._configValue = 50;
}
get configValue() {
return this._configValue;
}
set configValue(value) {
this._configValue = value;
}
}
const config = new Configuration();
console.log(config.configValue);
config.configValue = 100;資料變成私有的 _configValue,外界只能透過 getter/setter 觸碰它。現在 setter 就是加驗證、記錄變更、觸發副作用的明確落腳點。
TIP
有了封裝之後,若要驗證,就在 setter 內加檢查;若要監看,就在 getter/setter 內記錄——重點是這些行為終於有了一個、且只有一個該放的地方。
🔑 Takeaways
- 重構資料比重構函式難;封裝變數把「重組資料」轉成較單純的「重組函式」。
- 把讀寫收攏成 getter/setter,得到單一觀測點來控制存取與修改。
- 三個時機:需要存取控制(驗證)、想集中管理、要強制不變式(快取、通知等副作用)。
- 真正不可變的資料可以跳過封裝——它讀不到過時值。
- 它是 可變資料 這個壞味道的標準解;與 以查詢取代暫存變數 是不同手法,別混用。
No notes yet — jot your takeaways or Q&A here.