初探 23種 設計模式 – when I first time use design pattern

幾個月前我讀了本關於在遊戲開發應用設計模式的書 <設計模式與遊戲開發的完美結合>,這是我第一次接觸 design pattern 設計模式 (求學期間還真沒聽過),才看了前幾章,我就想:這是甚麼!原來有這麼厲害的東西啊!

全書閱畢後,也嘗試實踐於我的專案之中,受益良多。不過也發現我對 設計模式 的熟悉度與瞭解還不夠充分,無法隨心所欲的發揮它的效用,於是又有了第二本書 <設計模式的解析與活用> 正要閱讀。在仔細閱讀這本新書之前,我回想了在這幾個月我實踐了多少設計模式,做下筆記,也算是第一篇書本的讀後心得。

 

單例模式 (Singleton)

最快上手,也最快形成濫用的設計模式。除了應該避免大量使用,忽略了資源管理外,使用時也要注意多執行續之下的處理,因為我在 Unity 中通常使用單執行續的方式來處理邏輯,這部分還沒有涉獵太深。

我常常將它與 Static Class 做比較取捨,因為兩者使用方式相似,不過 Static Class 我通常是為了可以全域呼叫函式而使用,盡量不在 Static Class 中放置 non-const 變數;Singleton 則是為了儲存全域皆會使用的變數或類別實體,會包含較複雜的成員變數。

Singleton 我主要用於:

  • Network 連線的管理類別,因為唯一性以及貫穿整個程式的存在,相當適合。
  • 程式設定檔,因為唯一性以及整個程式都會需要,同時成員的變動性使它不適合用 Static Class。
  • 多語言系統的主類別,也是因為唯一性以及整個程式都會需要,另外可以實現 Delay Loaded 避免同時加仔大量的文字設定檔。
  • Unity Scene 的管理類別,因為需要非同步載入場景,少數實作在 MonoBehaviour 之上的 Singleton。
  • 結合 Observer 模式,建立 GameEventSystem,目的是希望在不斷轉換場景的情況下,有些遊戲事件會被繼續傳遞下去,只是要記得在轉換場景之間主動退訂已經不需要的事件,避免 null 例外錯誤。

 

狀態模式 (State)

我認為應用最廣泛且相當強大的模式,可以將落落長的程式碼做適當的切分,避開大量的 enum 以及 switch / if-else,並且有著相當程度的擴充彈性,缺點是產生與之對應數量的類別。

在 Unity 中,我將整個遊戲的流程切割成一個個的步驟後,每個步驟皆可以封裝成一個狀態(State),讓流程修改的過程變得相當容易,同時可以結合場景跟 UI 介面的轉換、GameEvent 的訂閱或退訂,做妥善的管理。

State 我主要用於:

  • Unity Scene 的管理,狀態的切換便會對應場景的切換,是書本中的應用實例。
  • 遊戲流程的管理,擴充第一點的功能,讓每個 Scene 之中可能會發生的主要流程都有專屬的 State,不同流程步驟或許會共用場景、共用某些物件,利用 State 可以妥善的管理與切換邏輯。
  • 角色動作的管理,Unity 的動畫本身就是使用狀態(State)來表現不同的動作,因此在控制角色時也使用 狀態模式, 來區分不同動作所需要的遊戲邏輯。
  • 多段控制器的使用,比如遊戲搖桿是使用 0 到 1.0 來表現移動的強度,用於接收搖桿訊號的類別便可以用 狀態模式 來處理不同強度的對應邏輯,取代冗長且不易維護的 Switch 區塊,並有著暫存的效果。

 

策略模式 (Strategy)

常常跟狀態模式搞混,老實說因為抱持能用就好的心態,並不會刻意去區分這兩者,上面 狀態模式 的例子或許有些也應該歸類於 策略模式。硬要區分的話,我認為 狀態模式 有較多狀態間切換的情況,而 策略模式 可能是為了讓同一個類別可以適用於數種情境,但保有大多數相同的部分,所以設定好使用的策略後就不會輕易去變動。

Strategy 我主要用於:

  • 網路封包的處理,不同專案或不同遊戲或許會有不同的規格,但同樣是為了處理封包,會有相當大的部分的邏輯是相同,將這些相同的地方開發完畢後,為了移植到不同的專案中使用,便將有差異的部分用 策略模式 進行切分,新專案之中只要重新實作不同的處理策略,而不需要重新實作封包接收、管理與資源釋放。
  • 玩家輸入的處理,與上點相同,需要重新實作的只有對應玩家操作時,所應該產生的回饋反應,訊號的暫存與過濾不需要重新實作。
  • GameEventSystem 的沿用,因為 GameEventSystem 太好用了,我在不同遊戲會不斷的沿用它,但是不同遊戲所需要的 GameEvent 並不相同,因此我用策略模式來為不同遊戲制定不同的 GameEvent,至於訂閱與退訂的流程我就可以完全不修改的直接使用。
  • 遊戲規則的變化,一個遊戲之中可能會有不同的遊戲模式,需要實作不同的規則,但是畢竟還是同一個遊戲,一定會有很多不會改變的地方。這例子也可以使用 樣板方法模式(Template Method) 來實作,不同的地方就用 Delegate 來進行設定。

 

其他實作過的模式

仲介者模式 (Mediator):

  • UI 的統一管理,一個場景中可能會有多個 UI 介面需要時常開關,或者 UI 及遊戲元件之間會有所互動,為了各項元件之間不需要儲存大量的 Reference 來呼叫,可以有個統一管理的機制。
  • 遊戲系統的溝通,同樣也是為了每個系統可以獨立,而不需要儲存其他系統相關的變數。

封裝模式 (Facade):

  • 網路引擎的封裝,負責網路工作的類別較多,連線的管理、封包的管理、硬體資訊的管理等,全部封裝在一個類別底下,可以將複雜的工作歸納成一個個名稱明確的函式,同時對於特定工作的修改也可以輕易知道該在哪裡維護。

觀察者模式 (Observer):

  • GameEventSystem 的實作,可以讓各個系統之間有個互相通知的管道。

享元模式 (Flyweight):

  • 對遊戲資源的統一管理,許多遊戲資源的載入,如圖片、音效、語言包等可以在不同物件之間共用,統一進行載入以及釋放的管理可以避免重複載入,或者 Unity 自動回收資源時造成遊戲 Lag。

樣板方法模式 (Template Method):

  • 遊戲規則的變化。
  • UI 元件的觸發事件,我實作了 uGUI 的事件接收類別,可以在程式上對 UI 元件的回饋進行註冊,但是 UI 動畫等是保持不變的。

 

不須特別實作,便在 Unity 中體現出來的設計模式

原型模式 (Prototype):

  • 將物件建立成 Prefab,並在遊戲執行中需要時將之實體化。

組合模式 (Composite):

  • Unity 物件在場景中 Parent 及 Children 的關係便是 Composite 的結構。
  • 當成是對物件有批量的動作要運作時,可以善用物件的 Parent – Children 結構。
  • 我通常會將同一個 UI 下的組件用一個 Empty Object 作為 Root Parent,再透過 Aactive 這個 Root 來做整個 UI 的開關。

迭代器模式 (Iterator):

  • 如今這個模式通常會內建在程式語言中了,C# 內建的資料結構 (List、Dictionary 等) 都有支援 foreach。

 

後話

自從開始使用設計模式之後,幾乎是顛覆了我過去寫程式的習慣,真正開始產出具有 可維護性 可複用性 的程式設計。為此我花了一段時間重新思考我使用 Unity 的方式,度過了幾乎沒有產出的一個月,但後來便感受到所花費的時間是值得的,大幅加速了接下來所進行的所有程式開發。

如今每隔一段時間,新功能的開發到達一個階段之後,我便會開始思考如何更好的利用設計模式在進行的專案之中,並且花上一些時間進行 重構 (Refactoring) 工作。這些額外所花的心力,會大幅降低下階段新功能的開發難度,進而節省相關的開發時間,可說是穩賺不賠。並且經過一段時間,會累積一定量的泛用功能,可以大幅加速接下來的每個新專案,可以說是一個不斷獲得利息的投資手段呀!

設計模式到目前為止我實作應用了一半以上,但是完全沒有自信能說自己已經熟悉了,更何況是還沒實際使用過的部分,未來會繼續嘗試如何妥善使用各種設計模式,持續累積開發經驗。

 

 

附表:全部 23 種設計模式 (來自 design pattern 原著的表格)

table-designpattern.PNG

Advertisements

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s