Shared Mutable State(共享可變狀態)
共享可變狀態是一個值放在多方都讀得到、任一方都改得動的位置,而它的當前值決定其他地方的行為。控制器持有的欄位、服務層的快取、被多個畫面訂閱的狀態物件都屬於這一類。它跟 snapshot 相對:snapshot 凍結某一時刻的複本、不隨現在漂移;共享可變狀態則沒有版本,讀到的永遠是最後一次寫入的結果。
概念位置
共享可變狀態的代價不在讀寫本身,在於它會長出跨函式的時序約束:「這個值必須活過那一次重設」「A 要在 B 清空之前讀完」。這類約束是 invariant 的一種,但落點的處境不同——單一物件的不變式可以做進建構子或型別簽名,跨函式的時序約束在型別簽名裡沒有位置、建構子也檢查不到,剩下的落點只有一條會紅的測試(強制層的取捨見 不變式的強制層次)。
把值收成呼叫時傳入的參數,可見範圍縮到那一次呼叫,約束就不存在了。所以遇到這類約束時的第一個問題是「這個值需不需要共享」,而不是「誰來守這個順序」。
可被觀察不等於可被寫入。一個值要讓畫面訂閱(state stream 的形態)是讀側需求,跟「誰改得動它」是兩個獨立的決定。兩者被同一個欄位承擔時,「因為畫面要看所以只好共享」會被誤當成「所以也只好開放多方寫入」。
可觀察訊號
註解出現「重設時不要清掉這個值」「先讀再清」這類順序叮嚀時,共享可變狀態已經長出時序約束,而強制還停在文件層。測試要先擺好某個欄位才跑得起來、而那個欄位不在被測函式的參數列裡,是同一個訊號的測試側形態。同一個欄位的寫入點散在多個控制器或服務時,「當前值是誰寫的」已經無法從單一位置回答。
設計責任
先問這個值需不需要共享——能收成參數就消除整組約束,不需要任何一層守它。必須共享時把寫入點收斂到單一路徑(讀可以開放、寫要有唯一入口),並把「可觀察」與「可寫入」分開設計。約束消不掉時,交給一條會紅的行為測試,而不是交給一行叮嚀順序的註解——判準與當場可執行的驗證見 #253 寫註解的動機是怕被改壞時要處理的是那個約束。