less than 1 minute read

今天看到了一個重構案例,現在來說說。

遊戲裡有一套UI,用來顯示物品(好比說是裝備、道具)的資訊、圖片、還可以做進化、煉製等的功能,UI的擺設如下圖:

UI Layout

A1單純用來表現物品的圖片,A2是返回前頁的功能按鈕,B組顯示物品詳細資料以及包含一些動作按鈕,例如,B介面含有一個按鈕,會將介面切換為C組,做進化、煉製的操作。(而從C組返回B組則是透過A2上的按鈕。)

B1介面上擺放的是物品的選單圖示、過濾器等,由這邊驅動顯示物品的切換。C1會顯示C組需要的操作功能。

從擺設看,每個區域有各自負責的項目,分工很清晰,概念也很分明,但程式實作在互動與控制的流程上卻不是這麼回事。

因為美術素材使用、表現動態、以及效能上的考慮,介面被分成A,B,C三組,於是程式碼也因為固有結構,分成了三個UI物件。(UI_A, UI_B, UI_C)

UI_A 被賦予了展現物品、返回前頁、載入與切換 UI_B, UI_C 的職責。UI_B 除了顯示資訊外,還負責控制 UI_A 去切換物品、切換 UI_C。

互動的關係是這樣的:

Original Archecture

這裡面有兩個壞味道:

  • 1.UI物件的職責太多了
  • 2.控制流的方向是雙向的

兩個壞味道可以分開處理,不過一起處理的架構更乾淨。

先從第2點看。雙向的控制流可以透過事件、或注入的方式改成單向,而固有的程式碼架構中已經存在了事件處理機制,所以可以套用事件機制。套用後,UI_B 不再需要控制 UI_A,改成發送它狀態變化的事件。

但現在監聽這些事件的任務被放在了 UI_A 身上,因為它有載入與切換 UI_B, UI_C 以及切換呈現的物品的職責。

所以來看第1點。新增一個 Controller 物件,把 UI_A 單純化,只保留呈現物品的職責,其他任務都改放在 Controller 身上。由 Controller 負責控制 UI_A, UI_B, 與 UI_C(未來可能還有更多的D,E,F之類)。

同時, Controller 也會監聽切換物品、切換UI等的事件,再發送相對應的控制到 UI。

於是有了這樣的架構:

Refactored Archecture

改著改著,雖然並不是刻意,但其實就是朝著 MVC(Model-View-Controller) 模式前進。也可以說,因為學習這些架構與模式,經過消化吸收後成為養分,當情境符合,自然而然就會朝著它們前進…

後記 :

在重構前的程式碼中,是存在著 Controller 物件的,但它不但被賦予了不該有的顯示面的職責,與UI之間,也互有控制流的進出,造成更大的混亂。 這應該是設計時想要套用 MVC 模式,但卻有些偏差所導致。

Comments