開發客戶端程式時,有些時候後端傳過來的資料根據情況變化,畫面也有所不同。舉例來說,當使用者登入系統時,後端會根據使用者的權限等級傳送不同的資料,使畫面呈現出不同的功能和選項。另一個例子是,在我們的開發的遊戲中,顯示比賽列表的畫面,也會根據不同的場次型態,顯示不同的卡片樣式。
當回傳資料不一致時,會發生許多 if 判斷式散佈在各個地方,這就是所謂的 Shortgun Surgery 問題。換句話說,當你需要修改一個功能時,你需要在多個地方進行修改,這樣會導致代碼的耦合性增加,並且增加了代碼的維護成本。
在我們的遊戲中,後端會給前端每一場比賽資料,讓前端可以顯示比賽資訊。在下圖中,我們從後端接收了一個 Json 物件,然後把 Json 物件轉換成 Contest 物件,最後一路傳到 View,給 View 決定如何顯示畫面。
在上面的例子中,不同的 Contest 型態,有不同的卡片樣式,當 Contest 的類型不同,後端可能也會根據不同的 Contest 而給前端不同資料,假設我們使用的強型別的語言,那我們到底要如何把不同的 Json 轉成同一個物件呢?
最簡單解決問題的方式,我們可以在 Contest 放上所有可能的出現的資料,View 則是根據自己的需要選擇相對應的欄位使用。以 Practice Contest View 來說,他只會使用 type 與 spots,但對 Non-Pratice Contest 來說,則是所有欄位都會使用。
另一個例子是,在下面的畫面中,可以發現大部分的畫面樣式都相同,唯一不同的是球員的資訊,不同球類的球員,顯示不同的數值來展示球員的過去表現。以板球球員來說,我們使用 Batting 與 Bowling 表示球員綜合表現,以足球來說,則是使用 Points 表示。
此時大部分的資料都是類似的,只有少部分關於球員表現的資料略微不同,我們可以使用轉接器模式,把後端傳回來的資料,轉換成另一種格式,讓畫面可以用一致的方式操作資料。
以球員表現的例子來説,我們可以把新增一個 Map,用以儲存每種球員數據的 Enum 與其數字,View 在使用時,就可以顯示 Map 中的所有數據即可,而無須關心到底是板球還是足球。
在我們的遊戲中,我們有許多不同的廣告,有時是單純的一張圖,有時是比賽資訊,而後端則是根據不同情況傳回不同資料。與球員數據問題類似,我們都接收來自後端不大相同的資料,但不同的是,這些資料幾乎沒有共通性,我們難以使用轉接器模式整理出一致的格式。
為了解決這個問題,我們可以使用策略模式,提供一個包含 buildComponent 的方法的介面。當 AdView 從 Controller 取得 Ad 物件時,呼叫 Ad 物件身上的 buildComponent 方法取得顯示用的 Component。 當我們這樣做之後,View 再也無需關心現在到底是哪一種廣告,只需要呼叫 buildComponent 即可取得相對應的廣告畫面,並把它塞進畫面中即可。
在 GeneralAd 與 ContestAd 中,它們會各自實現 buildComponent,提供該類型廣告的 Widget 給 View 顯示。
由於客戶端畫面可能會相當多變,後端可能會給一個 type,不同 type 所包含的資料格式也大不相同,如何處理這個資料與畫面的關係也沒有標準答案,即便問題相似,但是在不同的 Context 之下,也適合不同的解法,只有根據當下情況選擇適當的做法,才能讓後續開發與維護更加順利。