本章介紹七種 Web 展示層模式:Model View Controller、Page Controller、Front Controller、Template View、Transform View、Two Step View 與 Application Controller。它們涵蓋了從輸入控制器的組織到視圖渲染策略的各種設計選擇。
Model View Controller#
將使用者介面互動拆分為三個不同的角色。
意圖#
Model View Controller(MVC) 是最常被引用(也最常被誤引)的模式之一,由 Trygve Reenskaug 在 1970 年代末為 Smalltalk 平台開發。MVC 定義三個角色:Model(模型)是包含所有資料與行為的非視覺物件;View(視圖)是模型在 UI 中的顯示;Controller(控制器)接收使用者輸入、操作模型並更新視圖。
運作方式#
- MVC 涉及兩個主要的分離:展示層與模型的分離,以及視圖與控制器的分離
- 展示層與模型的分離是最重要的設計原則,理由包括:
- 展示與模型是本質上不同的關注點,使用不同的程式庫
- 同一個模型可以支援多種展示方式(Rich Client、Web 瀏覽器、遠端 API、命令列介面)
- 非視覺物件更容易測試
- 依賴方向是關鍵:展示層依賴模型,但模型不依賴展示層
- 在 Rich Client 環境中,多個視圖可能同時顯示同一模型的資料,透過 Observer 模式(事件傳播或監聽器)保持同步
- 視圖與控制器的分離較不重要,實務上大多數 GUI 框架將兩者合併;但在 Web 前端中,控制器與視圖的分離又變得有用
許多人將 Application Controller 誤認為 MVC 中的 Controller。兩者是完全不同的概念:MVC Controller 處理使用者輸入;Application Controller 處理畫面導航與流程邏輯。
何時使用#
- 展示層與模型的分離幾乎永遠都應該做,只有在非常簡單且模型沒有真正行為的系統中才可以跳過
- 視圖與控制器的分離在 Rich Client 系統中通常不需要,但在 Web 前端中很常見,本章大多數 Web 模式都基於此原則
Page Controller#
處理 Web 網站上特定頁面或動作請求的物件。
意圖#
Page Controller 為 Web 網站的每個邏輯頁面提供一個輸入控制器。控制器可以是伺服器頁面本身(如 ASP、JSP、PHP),也可以是對應該頁面的獨立物件(如 Servlet)。
運作方式#
- 基本概念是一個模組對應 Web 網站上的一個頁面,更精確地說是對應每個動作(點擊連結或按鈕)
- Page Controller 可以是伺服器頁面(將 Page Controller 和 Template View 放在同一檔案中)或腳本(如 Servlet)
- 使用伺服器頁面時,頁面邏輯容易變成混亂的 scriptlet 程式碼,因此建議使用 helper 物件將邏輯抽離
- Page Controller 的基本職責:
- 解碼 URL 並擷取表單資料
- 建立並呼叫模型物件處理資料(模型物件不應依賴 HTTP 請求)
- 決定要顯示哪個視圖並將模型資訊傳遞給視圖
- 可以混合使用伺服器頁面和腳本:簡單的 URL 用伺服器頁面,複雜邏輯的 URL 用腳本

Figure 14.1: Page Controller servlet with JSP view
何時使用#
- 適合:網站中大多數控制器邏輯相當簡單的情況,大部分 URL 可以用伺服器頁面處理,複雜的再用 helper
- 不適合:當導航邏輯複雜時,Front Controller 的優勢更明顯
- Page Controller 與 Front Controller 可以在同一網站中混合使用,且從 Page Controller 重構到 Front Controller 也很常見
Front Controller#
處理 Web 網站所有請求的單一控制器。
意圖#
Front Controller 將所有請求處理集中到一個 handler 物件中,該物件可以執行共通行為,再將請求分派給 command 物件執行特定行為。
運作方式#
- 由兩部分組成:Web handler 和 command 階層
- Web handler 接收 Web 伺服器的 POST/GET 請求,從 URL 提取足夠資訊來決定要啟動哪個 command,然後委派執行
- Web handler 幾乎總是實作為類別而非伺服器頁面,因為它不產生任何回應
- 分派可以是靜態的(解析 URL 並用條件邏輯)或動態的(從 URL 取出標準部分來動態實例化 command 類別)
- 靜態分派有編譯期檢查的優點和 URL 的靈活性
- 動態分派允許不修改 handler 就新增 command
- 可搭配 Intercepting Filter(裝飾者模式)包裝 handler,建立 filter chain 處理驗證、日誌、國際化等
- 因為每個請求建立新的 command 物件,不需要擔心 command 類別的執行緒安全性

Figure 14.2: How the Front Controller works
Front Controller 的一個關鍵優勢是只需要在 Web 伺服器配置一個 handler,其餘由 handler 分派。這在 Web 伺服器配置困難或需要動態新增 command 時特別有價值。
何時使用#
- 適合:網站有許多共通行為需要統一處理(安全、國際化、特定使用者的視圖),或導航較複雜的情況
- 不適合:若控制器邏輯簡單,Page Controller 更直觀且開銷更小
- 可以用 Decorator 在執行時期動態增強控制器行為(驗證、字元編碼、國際化等)
Template View#
透過在 HTML 頁面中嵌入標記來將資訊渲染成 HTML。
意圖#
Template View 以靜態 HTML 頁面為基礎,在其中放入可被動態資訊替換的標記(marker)。頁面的靜態部分作為回應的模板,標記與真正的程式溝通以填入結果。
運作方式#
- 最常見的形式是 伺服器頁面(ASP、JSP、PHP),它們在基本 Template View 之上更進一步允許嵌入任意程式碼(scriptlet)
- 嵌入大量 scriptlet 的最大問題:
- 排除非程式設計師編輯頁面的可能性
- 頁面作為程式模組的結構很差
- 太容易將不同層的邏輯混在一起
- 解決方案是使用 helper 物件:頁面只包含對 helper 的呼叫,helper 持有所有真正的程式邏輯
- 條件顯示應避免使用通用的
<IF>標籤,改用更聚焦的條件標籤(如<highlight condition="isHighSelling">),且條件應基於 helper 的單一 Boolean 屬性 - 迭代通常需要集合標籤,最佳方式是使用專門的迭代標籤而非 scriptlet 迴圈
何時使用#
- 適合:允許透過頁面結構組織內容,設計師與程式設計師可以協作,學習門檻低
- 不適合:需要良好的紀律保持頁面簡單且以顯示為導向;Template View 比 Transform View 更難測試(大多數實作需要在 Web 伺服器中運行)
- 選擇視圖時也要考慮 Two Step View——若使用 Template View 實作 Two Step View,可能需要特殊化的標籤
Transform View#
逐一處理領域資料元素並將其轉換為 HTML 的視圖。
意圖#
Transform View 從領域和資料源層取得資料後,透過轉換程式將領域資料作為輸入、HTML 作為輸出。與 Template View 以輸出為中心組織不同,Transform View 以輸入元素為中心組織。
運作方式#
- 程式走訪領域資料的結構,辨識出每種領域資料形式後,輸出對應的 HTML
- 主流選擇是 XSLT——一種函數式程式語言,類似 Lisp、Haskell
- 要進行 XSLT 轉換,需要先將領域資料轉為 XML(可透過 Data Transfer Object 序列化,或 Transaction Script 直接回傳 XML)
- Transform View 的規則可以任意排列而不影響輸出結果
- 避免了 Template View 的兩大問題:更容易保持轉換聚焦於渲染 HTML,也更容易測試(不需要 Web 伺服器)
何時使用#
- 適合:團隊偏好 XSLT 或需要跨平台(J2EE 或 .NET 產生的 XML 用同一個 XSLT 轉換)
- 不適合:XSLT 工具比 HTML 編輯器少得多,XSLT 的函數式風格加上 XML 語法使學習曲線較陡
- 若需要全域外觀變更或支援多種外觀,應考慮 Two Step View
- XSLT 在處理 XML 文件時特別自然,若資料本身就是 XML 則更合適
Two Step View#
將領域資料分兩步轉為 HTML:先形成某種邏輯頁面,再將邏輯頁面渲染為 HTML。
意圖#
Two Step View 將轉換過程拆成兩個階段。第一階段將模型資料轉換為不含任何 HTML 的邏輯畫面結構(presentation-oriented structure);第二階段將該邏輯結構轉換為實際的 HTML。
運作方式#
- 第一階段為每個頁面獨立撰寫,負責存取領域模型並將資訊放入邏輯畫面結構(如 field、header、footer、table、choice 等元素)
- 第二階段由整個系統共享,知道如何將邏輯畫面結構中的每個元素渲染為 HTML
- 邏輯畫面結構是展示導向的(presentation-oriented),強制畫面遵循一致的樣式
- 實作方式:
- 兩階段 XSLT:第一階段樣式表將領域 XML 轉為畫面 XML,第二階段樣式表將畫面 XML 轉為 HTML
- 類別方式:定義 table class、row class 等邏輯結構類別,第一階段實例化這些類別,第二階段渲染為 HTML
- Template View + 自定義標籤:JSP 頁面只使用自定義的邏輯畫面標籤(如
<2step:title>、<2step:field>、<2step:table>),標籤實作負責輸出 HTML

Figure 14.5: Sequence diagram for two-step rendering
Two Step View 的核心限制:最終的 HTML 必須能從邏輯畫面結構推導出來。若網站每個頁面的設計都很不同,就很難找到足夠的共通性來建立統一的邏輯畫面結構。
何時使用#
- 適合:
- 多外觀應用程式:同一功能提供給不同組織,每個組織有不同的外觀。十個畫面三種外觀,單階段需要 30 個視圖模組,Two Step View 只需要 10 個第一階段 + 3 個第二階段

Figure 14.6: Single-stage view with one appearance

Figure 14.7: Two-stage view with one appearance

Figure 14.8: Single-stage view with two appearances

Figure 14.9: Two-stage view with two appearances
- 單外觀應用程式:想要全站一致的外觀且容易全域修改
- 不適合:設計密集的網站(每個頁面外觀差異大)、需要 WYSIWYG 工具的設計師團隊
- 也可以為不同裝置(瀏覽器 vs PDA)提供不同的第二階段,但兩者必須遵循相同的邏輯畫面結構
Application Controller#
處理畫面導航和應用程式流程的集中控制點。
意圖#
Application Controller 集中管理應用程式中「該使用哪個畫面」和「該執行哪個領域邏輯」的決策。輸入控制器(如 Page Controller 或 Front Controller)向 Application Controller 詢問要執行的 command 和要使用的 view。
運作方式#
- 有兩個主要職責:決定要執行哪個領域邏輯,以及決定要顯示哪個視圖
- 通常持有兩個結構化的集合:一個是領域 command 的類別參考,一個是視圖的參考

Figure 14.10: Application controller references
- 領域 command 可以是 Application Controller 層的 command 物件、Transaction Script 的參考,或領域物件方法的參考
- 建議 Application Controller 不要依賴 UI 機制,使其可獨立測試且可被多種展示層重用
- 一個應用程式可以有多個 Application Controller,每個負責 UI 的不同區域
- UI 常被視為狀態機——某些事件根據關鍵物件的狀態觸發不同的回應。Application Controller 特別適合用 metadata(透過程式碼或設定檔)來表達狀態機的控制流程
書中範例使用資產(asset)的狀態模型:資產在「租賃中(On Lease)」、「維修中(In Repair)」和「庫存中(In Inventory)」三個狀態間切換。Application Controller 根據 command 字串和資產狀態的組合,查找對應的 domain command 類別和視圖 URL。
何時使用#

Figure 14.11: State diagram for an asset
- 適合:應用程式有精靈式(wizard)的互動流程,或畫面的順序和條件依賴於物件狀態
- 不適合:若流程和導航夠簡單(任何人都能以任意順序造訪任何畫面),Application Controller 沒有價值
- 一個好的訊號是:當你發現應用程式的流程變更需要在很多不同地方做類似修改時,就該考慮 Application Controller
