概述#

企業應用程式近年來最大的變化之一,就是以 Web 瀏覽器 為基礎的使用者介面興起。Web 應用程式具備多項優勢:不需安裝客戶端軟體、統一的 UI 方式,以及便捷的通用存取能力。

準備一個 Web 應用程式,從 Web 伺服器軟體 本身開始。Web 伺服器通常有一份設定檔,指出哪些 URL 由哪些程式處理。在 Web 伺服器中,結構化程式主要有兩種形式:腳本(script)伺服頁面(server page)

  • 腳本形式:以函式或方法處理 HTTP 請求,例如 CGI 腳本與 Java Servlets。腳本解析 HTTP 請求物件取得資料,並以串流操作輸出 HTML 回應。
  • 伺服頁面形式:以回傳的 HTML 頁面為結構中心,在 HTML 中嵌入程式碼片段(scriptlets)在特定位置執行,例如 PHP、ASP、JSP。

腳本形式最適合處理請求的解讀,伺服頁面最適合格式化回應。因此,自然的做法是結合兩者:用腳本負責請求解讀,用伺服頁面負責回應格式化。這種分離最早以 Model View Controller(MVC)模式體現。

MVC 的核心要旨是:非呈現邏輯應被抽離出來。在 Web 呈現的脈絡下,一個請求進入 input controller,它從請求中擷取資訊,再轉交給適當的 model 物件。Model 物件與資料來源溝通,完成請求所要求的一切操作並收集回應資訊。完成後控制權回到 input controller,由它決定使用哪個 view 來顯示回應,並將回應資料一併傳遞給 view。

Figure 4.1: How MVC roles work together

套用 MVC 最首要的理由,是確保 model 與 Web 呈現完全分離。這使得修改或新增呈現方式更容易,也讓將處理邏輯分離到獨立的 Transaction Script 或 Domain Model 物件中變得可行,方便獨立測試。

另外還有 Application Controller 的概念,它處理應用程式的流程(flow),決定畫面的出現順序。當系統有大量關於畫面順序與導航的邏輯時,Application Controller 特別有用。判斷標準是:如果是機器控制畫面流程,就需要 Application Controller;如果是使用者控制,則不需要。

View Patterns#

在 view 端有三種模式可供考量:Transform ViewTemplate ViewTwo Step View

Template View vs. Transform View#

  • Template View:在頁面結構中撰寫呈現內容,並嵌入標記(markers)來指示動態內容的位置。許多流行的平台基於此模式(ASP、JSP、PHP),它們允許在頁面中嵌入完整程式語言,提供很大的靈活性,但也容易導致難以維護的混亂程式碼。使用伺服頁面技術時,必須非常自律地將程式邏輯從頁面結構中抽出,常藉由 helper 物件來實現。
  • Transform View:採用轉換風格的程式,典型範例是 XSLT。當領域資料已是 XML 格式或能輕易轉換為 XML 時特別有效。Input controller 選擇適當的 XSLT 樣式表並套用到從 model 取得的 XML 上。

大多數腳本會遵循 Template View 或 Transform View 其中一種風格,也有一些有趣的混合方式。

單階段 vs. 兩階段視圖#

  • 單階段視圖(single-stage view):每個畫面有一個 view 元件,取得領域導向的資料並直接渲染成 HTML。大部分時候可將其視為每個畫面一個 view。
  • Two Step View:將流程拆成兩個階段——第一階段從領域資料產生邏輯畫面(logical screen),第二階段再將邏輯畫面渲染為 HTML。整個應用程式只有一個第二階段物件。

Figure 4.3: A two-stage view

Two Step View 的優勢在於將「使用什麼 HTML」的決定集中在一處,使全域的 HTML 變更只需修改一個物件。它特別適合:

  • 不同畫面使用相同基本佈局的網站
  • 多個前端客戶共用同一系統但需不同外觀(如多家航空公司共用訂位系統)
  • 需要針對不同輸出裝置(如瀏覽器與掌上型設備)使用不同的第二階段

Input Controller Patterns#

Input controller 有兩種模式:

Page Controller#

最常見的做法是每個頁面一個 input controller 物件。最簡單的情況下,Page Controller 可以就是伺服頁面本身,將 view 與 input controller 的角色合併。在許多實作中,將 input controller 拆分為獨立物件會更容易維護。更精確地說,每個動作(action)——如一個按鈕或連結——對應一個 Page Controller。

Input controller 有兩個職責:處理 HTTP 請求與決定要做什麼。伺服頁面可以處理請求,再委派給一個獨立的 helper 物件來做決策。

Front Controller#

Front Controller 更進一步地實現分離:只有一個物件處理所有請求。這個單一處理器解讀 URL 來判斷請求類型,建立相應的處理物件來處理它。這樣可以將所有 HTTP 處理集中在一個物件中,避免每次變更網站的 action 結構時都需要重新設定 Web 伺服器。

選擇建議:

  • 網站偏向文件導向,有靜態與動態頁面的混合 → Page Controller
  • 導航和 UI 較為複雜 → Front Controller

Further Reading#

關於 Java Web 設計的優秀討論可參考 [Brown et al.] 的 Chapter 9。進階模式的最佳來源是 [Alur et al.],其中大多數模式也可用於非 Java 環境。關於 input controller 與 application controller 的術語分離,來自 [Knight and Dai]。