UI 架構(gòu)演進的本質(zhì):從“操作界面”到“推導(dǎo)界面

一、UI 架構(gòu)的定義

UI 系統(tǒng)的作用是把“用戶操作和數(shù)據(jù)變化”映射為“界面變化”。

從輸入(Action+data)到輸出(UI)包含了諸多工作,比如有:

  • 事件分發(fā)與意圖解析
  • 數(shù)據(jù)獲取與聚合
  • 業(yè)務(wù)邏輯處理
  • 界面渲染
    ...等等

我們進行架構(gòu)設(shè)計就是去:

  • 確定設(shè)立哪些功能模塊;
  • 確定每一項工作交給哪個模塊負責;
  • 確定各個模塊之間的依賴關(guān)系和通信方式;

用一句話總結(jié)

UI架構(gòu)就是UI系統(tǒng)中各模塊職責劃分、依賴關(guān)系以及數(shù)據(jù)流轉(zhuǎn)方式的整體設(shè)計。

UI 架構(gòu)解決的問題是—如何把“用戶意圖(操作)和數(shù)據(jù)變化”高效、穩(wěn)定地映射為“界面變化”。


UI 架構(gòu)的演化本質(zhì)上是為了應(yīng)對不斷增長的復(fù)雜度。

當“數(shù)據(jù)變化”越來越頻繁、“用戶交互”越來越復(fù)雜時,采用原有架構(gòu):

  • 開發(fā)成本和難度不斷上升;
  • 系統(tǒng)運行時候的穩(wěn)定性和效率不斷下降;

于是架構(gòu)不斷演進——


二、UI 架構(gòu)演化五階段

?? 第一階段:無架構(gòu) / 混沌階段

?? 核心思想

對每個UI 組件,逐個編碼操作。

整個業(yè)務(wù)流程+UI操作在代碼層是混合在一起的,具體每個業(yè)務(wù)需要操作哪些UI這些都是程序員的腦子在記憶。

textview = findView(R.id.tv)
textView.setText("Hello");

? 優(yōu)勢

  • 簡單直接
  • 易于上手
  • 微小項目開發(fā)效率高

? 問題

  • 數(shù)據(jù)和界面強耦合:當一個數(shù)據(jù)變化,關(guān)聯(lián)頁面很多,要手動更新五六個按鈕,極其容易漏掉,導(dǎo)致“顯示不一致。

  • 多人協(xié)作容易混亂

  • 業(yè)務(wù)邏輯不可復(fù)用

?? 演化契機

項目變大,代碼亂到無法維護時候,開發(fā)者開始意識到—UI 和邏輯不能混作一團,于是引入分層思想。


?? 第二階段:分層架構(gòu)(MVC / MVP)


?? 核心思想

UI 和邏輯要分離

  • Model:數(shù)據(jù)
  • View:界面
  • Presenter / Controller:邏輯

? 優(yōu)勢

  • 解耦 UI 和業(yè)務(wù)邏輯
  • 提升可維護性
  • 更適合團隊開發(fā)

? 問題

  • 接口爆炸(IView、IPresenter)
  • 各個模塊相互引用,內(nèi)存泄漏風險高
  • 雙向調(diào)用,數(shù)據(jù)流混亂

?? 演化契機

開發(fā)者逐漸意識到:

問題不在“分層”,而在決定UI的因素沒有被顯式建模和統(tǒng)一管理,

例如

  • 加載中?還是加載失???還是加載成功?
  • 用戶點過按鈕了嗎?
  • 當前展示的是舊數(shù)據(jù)還是新數(shù)據(jù)?
  • 多個請求回來,哪個是“最終結(jié)果”
    ...等等

這些控制UI的因素是隱式地分散在 UI 容器、業(yè)務(wù)邏輯、數(shù)據(jù)層、異步流程以及用戶交互等多個維度中,系統(tǒng)缺乏統(tǒng)一的ui更新因素輸入源,從而引發(fā)復(fù)雜性和大量不可預(yù)測問題。

這就好比:一個基層執(zhí)行人員每天的工作是由公司幾十個領(lǐng)導(dǎo)指派,那就很容易出現(xiàn)各種問題,比如任務(wù)前后矛盾,任務(wù)過載堵塞等。

?? 第三階段:狀態(tài)驅(qū)動(MVVM)


?? 核心思想

  1. 引入狀態(tài)概念:狀態(tài) = 在某一時刻,完整決定 UI 長什么樣的最小信息集合。
  2. 狀態(tài)被顯性建模,并指出:UI = f(State)
public class MyViewModel(
    // 狀態(tài)定義Start
    public MutableLiveData<Boolean> isLoadingLiveData = new MutableLiveData<>();
    public MutableLiveData<List<Item>> showListLivaData = new MutableLiveData<>();
    public MutableLiveData<String> errorLiveData = new MutableLiveData<>();
    // 狀態(tài)定義End

)

從此UI 的顯示邏輯由 state 統(tǒng)一控制推導(dǎo),而不是分散在各處手動操作。

? 優(yōu)勢

  • 狀態(tài)被顯式建模
  • UI 變成“可推導(dǎo)”的
  • 更容易測試和維護

? 問題

  • 狀態(tài)可能出現(xiàn)非法組合:因為一個頁面狀態(tài)可以是“碎片化”的,它們之間沒有建立“關(guān)系約束”;
  • 狀態(tài)變化沒有約束,會出現(xiàn)從狀態(tài)A變化到一個錯誤狀態(tài)C的問題;
  • UI 更新仍然是手動同步:手工寫如何根據(jù)狀態(tài)怎么更新UI的操作。

?? 演化契機

開發(fā)者進一步思考:

當狀態(tài)變量多了,無統(tǒng)一管理,容易出現(xiàn)非法狀態(tài)——狀態(tài)整合

狀態(tài)量碎片化,導(dǎo)致狀態(tài)變化難以約束,狀態(tài)轉(zhuǎn)換頻頻出錯——狀態(tài)切換約束(狀態(tài)機)


?? 第四階段:單向數(shù)據(jù)流(MVI / Redux)

?? 核心思想

狀態(tài)整合

狀態(tài)變化必須可追蹤、可推導(dǎo)

State??? = Reduce(State?, Action)

?? 本質(zhì)是一個:狀態(tài)機

這一階段的本質(zhì)升級:

從:

“狀態(tài)存在”

變成:

狀態(tài)成為一個原子整體
狀態(tài)如何變化也被建模

把“狀態(tài)變化過程”徹底規(guī)范化

? 優(yōu)勢

  • 狀態(tài)變化可預(yù)測
  • 易于調(diào)試(可回放)
  • 消滅“非法狀態(tài)組合”

? 問題

  • UI 仍然需要手動更新
  • 模板代碼較多
  • 開發(fā)學習難度高

?? 演化契機

開發(fā)者發(fā)現(xiàn):

狀態(tài)管理已經(jīng)很不錯,但 是從狀態(tài)到UI的映射仍然是“手動同步”的


?? 第五階段:聲明式 UI(React / Compose)

代表:Jetpack Compose

?? 核心思想

UI = f(state)

之前階段 :UI = “如何從 A 變到 B”,在這個階段變?yōu)椤爱斍皯?yīng)該是什么樣”

  • 之前階段:關(guān)注“過程”
  • 第5階段:關(guān)注“結(jié)果”

? 傳統(tǒng) UI 的本質(zhì)

UI 是“可變對象”

你在做不斷修改它

? 聲明式UI的本質(zhì)

UI 是“計算結(jié)果”

你在做:每次重新算一個新的 UI

聲明式 UI 不是“控制 UI 的變化”而是“不斷給出 UI 的完整定義”,未被聲明的部分不會存在。

示例

@Composable
fun ListScreen(state: UiState) {
    when (state) {
        is Loading -> LoadingView()
        is Success -> ListView(state.data)
        is Error -> ErrorView()
    }
}

第一次狀態(tài)更新:Loading 狀態(tài)

when (state) {
is Loading -> LoadingView()
}

?? UI 樹:

Root
└── LoadingView

狀態(tài)變成 Success

when (state) {
is Success -> ListView()
}

?? 新 UI 樹:

Root
└── ListView

?? 關(guān)鍵發(fā)生了什么?

Compose 在內(nèi)部做了這件事:

舊樹: LoadingView
新樹: ListView

→ diff
→ 刪除 LoadingView
→ 添加 ListView

?重點來了

?? 你沒有寫:

hide(LoadingView)

?? 但系統(tǒng)幫你做了:

從 UI 樹中移除

?? 這就是“聲明式”的真正含義

? 傳統(tǒng)命令式UI

你:把 LoadingView 隱藏

? 聲明式

你更新State,Loading is false

?? 系統(tǒng)推導(dǎo)出:現(xiàn)在 UI 不應(yīng)該有 LoadingView,

那就刪掉LoadingView

? 優(yōu)勢

  • UI 自動與狀態(tài)一致
  • 不再需要手動同步
  • 消滅 UI 狀態(tài)錯亂
  • 更接近函數(shù)式編程

? 問題

  • 狀態(tài)設(shè)計難度上升(核心問題轉(zhuǎn)移)
  • 副作用管理復(fù)雜(除了更新 UI State 以外,所有會對外界產(chǎn)生影響的操作,都是副作用,比如Toast、讀寫數(shù)據(jù)庫、修改全局變量、打日志等)
  • 學習成本較高
  • 容易寫出“偽聲明式代碼”

?? 演化契機

問題再次升級:

?UI 已經(jīng)很好,但狀態(tài)建模成為瓶頸


三、傳統(tǒng)UI架構(gòu)演化的終極目標

從整個歷史來看,UI 架構(gòu)一直在逼近一個目標:

?? 終極形態(tài)

UI = f(State)
State = g(UserIntent, Data)

?? 核心特征

  • UI 完全由狀態(tài)決定
  • 狀態(tài)變化可預(yù)測
  • 無副作用污染(狀態(tài)更新時候只更新 UI State ,不會有對外界產(chǎn)生影響的其他操作)
  • 系統(tǒng)自動完成更新

?? 用一句話總結(jié):

當 UI 完全由狀態(tài)決定,狀態(tài)變化完全由可追蹤的行為驅(qū)動時,系統(tǒng)就從“不可控的黑箱”,變成了一個“可以被理解、被推演、甚至被回放的白箱系統(tǒng)”——這正是 傳統(tǒng)UI 架構(gòu)演化的終點。

但現(xiàn)在AI來了,一切又不一樣了


四、AI時代的演化方向

開發(fā)者不想再“手寫狀態(tài)機”

MVI / Redux 本質(zhì)是:

?? 人類在手動維護一個“狀態(tài)機”

但問題是:

  • 狀態(tài)爆炸(State explosion)
  • 分支復(fù)雜(if / else 地獄)
  • 維護成本極高

?? 本質(zhì):

? 人在“模擬智能”,但人并不擅長這個

AI(尤其是大模型)帶來了一個非常關(guān)鍵的能力:

?? 從“規(guī)則驅(qū)動”變成“意圖驅(qū)動”

過去:

用戶點擊 → Action → reducer → State → UI

現(xiàn)在開始變成:

用戶表達 → AI理解(Intent) → 生成狀態(tài) / UI → 渲染

?? 關(guān)鍵變化:

“狀態(tài)不再完全由人定義,而是可以被推導(dǎo) / 生成”

過去:

Human: 定義 State + Transition

現(xiàn)在:

AI: 推導(dǎo) State
Human: 定義約束(Constraint)

?? 核心變化不是“AI生成UI”

而是:

人從“構(gòu)造狀態(tài)機”,變成“約束狀態(tài)空間”

如果說過去的 UI 架構(gòu)是在解決“如何讓狀態(tài)不失控”,那么未來的 UI 架構(gòu),將是在解決——當狀態(tài)本身由機器生成時,人類如何依然掌控系統(tǒng)的邊界。

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

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

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