如何应用设计模式设计你的足球引擎
原文地址:
http://www.codeproject.com/KB/architecture/applyingpatterns.aspx
作者:An 'OOP' Madhusudanan
譯者:賴勇浩(http://blog.csdn.net/lanphaday )
譯者說:這是一篇非常好的文章,有非常棒的例子,非常棒的文筆,非常棒的代碼(VB.net編寫的,但你肯定讀得懂),如果你還不懂設計模式,那它肯定是最適合你的 DPs 文章之一。
第一部分
解決方案架構師:你可以嘗試使用模式
愚蠢的開發者:好的,它像 ActiveX 控件那樣用嗎?"
介紹
關于本文
本文希望能夠做到
- 以簡單、可讀的方式向你介紹模式
- 教你如何真正“應用”模式(模式易學,但必須有過硬的設計本領才能應用它們解決問題)
- 讓你認清應用 Builder、Observer、Strategy和 Decorator(這幾個可是少數極常用的模式)模式的時機。
- 展示如何用 Observer 模式解決設計難題
全文通過如下內容依次推進
先決條件
- 你需要懂得一些閱讀和理解 UML 圖的知識。
代碼使用指南
- 相應的 zip 文件包含了代碼、UML設計圖(visio 格式)等,你可以使用 Winzip 等壓縮軟件解壓。
簡說設計模式
即使對設計模式知之甚少,設計師和開發者也會傾向于重用類和對象間來簡化設計過程。簡言之就是“設計模式考慮了多種對象(類、關系等)間的協作”,為常見的設計問題提供解決方案。最為重要的是他們為設計師和程序員提供一些“行話”來談論他們的設計。例如你可以告訴你的朋友你使用了 Builder 模式來解決你項目中的一些問題。
Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides[即知名的四人幫(GOF)]為常見的設計問題提供了一致的分類模式。GOF 模式被認為是其它所有模式的基礎。
使用模式的基本原則是可重用性。如果你正確理解了以模式為中心的軟件工程概念,當遇到問題時你就不會重復發明輪子。這里有一些關于設計模式的重要觀點:
- 設計模式不是代碼,實際上它是一種解決問題的方法或模型。
- 設計模式是關于設計和對象間互動的,為它們提供解決常見設計問題的可重用的解決方案。
- 設計模式通常可以用 UML 圖來表示。
真正的動手的經驗可以給你更好的理念。
架構(簡單)足球引擎
假設你在一家游戲開發公司供職,上頭決定讓你為公司的重要項目——足球游戲引擎做一套解決方案架構(很棒,哈哈)。現在由你領導設計整個足球游戲引擎,突然你就多了許多要考慮的事情,比如:
- 在游戲系統中如何標識實體,
- 如何確定設計問題所在,
- 如何應用模式來搞定你的設計說明書?
標識實體
首先,需要標識游戲引擎中的所有對象。因此你要想像一下終端用戶將如何使用這個系統,現在假設終端用戶將用以下序列來操作游戲(先簡單化):
- 打開游戲
- 選擇兩支球隊
- 配置球員
- 選擇球場
- 開始
系統是可能有若干個球場(PlayGrounds)和球隊(Teams)。系統中實際上起碼有這些對象:
- 球員(Player),踢球的人。
- 球隊(Team),包含若干球員。
- 球(Ball),球員所持有的物體。
- 球場(PlayGround),比賽進行的地方。
- 裁判(Referee),球場上控制比賽的人。
另外,游戲引擎中還有一些邏輯對象,如:
- 游戲(Game),定義了足球比賽,制定球隊、球、裁判、球場等。
- 同時模擬一個或多個比賽。
- 球隊策略(TeamStragy),比賽時決定球隊的策略
這只是對系統的一個抽象形式,下圖表示了系統中的類的多樣性和它們之間的接連關系(“has”)。其中箭頭表示了閱讀的方向次序。游戲引擎(GameEngine)擁有若干比賽(Game);比賽(Game)有三個裁判、一個球、兩支球隊和一個球場;而球隊又有多個球員和一個策略產生器。
?
Fig 1 - High level view
確定設計問題
現在你要決定
- 這些對象如何組織
- 如何創建
- 如何在設計說明書中確切地闡述當他們彼此影響時的行為。
首先,你得寫下對足球引擎的最小描述來確定設計問題,例如下面是是對我們之前討論的一對象的設計問題
- 足球(Ball)
- 當球的位置變化,所有的球員和裁判應當能夠立即感知。
- 球隊與球隊策略(Team and TeamStrategy)
- 在比賽中,終端用戶可以改變球隊的策略(如從進攻改為防守)
- 球員(Player)
- 球隊中的球員還得有一些額外的職責,如前鋒、后衛等,應該可以在運行進指派這些職責。
- 球場(球場)
- 每一個球場要有座位、草皮、觀眾等,而且每一個球場都應該有不同的外觀。
現在讓我們想想該怎么確定模式以解決這些設計問題
確定要用的模式
再仔細看看(是的,最好多看幾次)上面確定的設計問題,現在讓我們想想怎么用設計模式來解決它們。
1: 解決與球(Ball)相關的設計問題
首先來看看關于球的說明,需要設計一個框架使得當球的狀態(位置)變化時能夠通知所有球員和裁判,以得到球的新狀態(位置),實際上就是:
特定的設計問題:當球的位置變態,馬上通知所有球員和裁判。
問題泛化:當主題(這里是指球)改變,所有的依賴物(在這里是指球員等)能夠自動獲得通知并更新。
當你遇到這樣的設計問題,應當馬上想起 GOF 模式,甚至立馬認識到可以用Observer 模式來解決問題。
觀察者模式(Observer Pattern):定義了對象間一對多的依賴關系,當一個對象的狀態改變,自動通知所有依賴對象并更新。
在這里我們使用這個模式是因為當球的位置變化時需要通知所有的球員。
2: 解決與球隊(Team)和球隊策略(TeamStrategy)相關的設計問題
然后,我們來解決球隊和球隊策略的問題。像之前討論的那樣,當比賽進行時,終端用戶能夠改變他的球隊的策略(如從進攻改為防守)。無疑地,這意味著我們需要把球隊策略從球隊中分離出來。
特定的設計問題:在比賽進行中終端用戶能夠改變它的球隊的策略(例如從進攻改為防守)
問題泛化:使客戶(在這里是球隊)能夠獨立地改變算法(球隊策略)
你可以選擇 Strategy 模式來解決上面這個設計問題。
策略模式(Strategy Pattern):定義一系列算法,通過封裝使它們可以互相替換,Strategy模式使用戶能夠獨立地改變算法。
3: 解決與球員(Player)相關的設計問題
現在讓我們來完成與球員相關的設計說明書。從我們的問題定義可以確定我們需要在運行時為每一個球員指派不同的職責(如前鋒、后衛等)。這時候我們可以考慮子類化(也就是繼承),通過創建一個球員類,然后從這個基類派生一些類,如前鋒、后衛等。但它的不足是當你子類化的時候,你不能從對象的實現中分離職責。
換言之,在我們的案例中子類化并非恰當的方法,因為我們需要從球員的實現中分離類似前鋒、中鋒、后衛等職責。原因在于球員在某一時刻是前鋒,而另一個時刻同一個球員又可以是中鋒。
特定的設計問題:球隊中的球員有額外的職責,如前鋒、后衛等,而且要能夠在運行時指派。
問題泛化:需要在對象(在這里是指球員)上動態附加額外職責(如前鋒、中鋒等),而且不可使用子類化。
那么你可以選擇 Decorator 模式來解決這個設計問題。
裝飾者模式(Decorator Pattern):在對象上動態地額外附加職責,Decorator 提供了子類化之外的靈活的擴展功能。
4: 解決球場(PlayGround)相關的設計問題
如果看去看看球場的說明,可以發現球場的外觀由多個子單元(如座位、草皮和觀眾等)決定。球場的外觀根據這些子單元的不同而不同,因此,我們需要特別的構建方式,它可以創建不同的球場。也就是說一個意大利球場應該有與英格蘭球場不同的座位結構和草皮,但游戲引擎卻可以通過調用相同的函數族來創建這些球場。
特定的設計問題:每個球場都由座位、草皮和觀眾等構成,但它們又有互不相同的外觀。
問題泛化:需要從對象(球場)的表示(球場的外觀)分離它的構建,還需要使用同樣的構建過程來創建不同的表示。
創建者模式(Builder Pattern):從復雜對象的表示中分離它的構建,從而使相同的構建過程能夠創建不同的表示。
現在,你可以選擇 Builder 模式來解決上面的設計問題。
第二部分
解決方案架構師:我叫你去學學模式
愚蠢的開發者:是的,現在我可以用模式開發一個足球引擎了
解決方案架構師:啊?你的意思是?!@@#!
應用 Observer 模式
在這一節,我們先深入學習 Observer 模式,然后應用模式來解決第一個設計問題。不知道你還記不記得第一個設計問題:
- 當球的位置變化,馬上通知所有的球員。
理解 Observer 模式
下面是 Observer 模式的是 UML 類圖:
?
Fig 2 - Observer Pattern
下面介紹一下這個模式的成員:
- 主題(Subject)
Subject類提供了掛上和拆卸觀察者的接口,并且持有一序列的觀察者,還有如下函數:
- Attach - 增加一個新的觀察者到觀察者序列
- Detach - 從觀察者序列中刪除一個觀察者
- Notify- 當發生變化時,調用每一個觀察者的 Update 函數來通知它們。
- 具體的主題(ConcreteSubject)
這個類提供了觀察者感興趣的狀態,它通過父類的 Notify 函數通知所有的觀察者。ConcreteSubject的函數有:
- GetState - 返回主題的狀態
- 觀察者(Observer)
Observer類為所有的觀察者定義了一個更新接口,用以接收來自主題的更新通知,它是一個抽象類,可以派生具體的觀察者:
- Update - 這是一個抽象函數,具體的觀察者會重載這個函數。
- 具體的觀察者(ConcreteObserver)
這個類維護了一個主題的引用,用來在收到通知的時候接收主題的狀態。
- Update - 這是具體類重載的函數,當主題調用它時,ConcreteObserver 調用主題的 GetState 函數來更新與主題狀態相關的信息。
應用 Observer 模式
現在讓我們來看看怎么用這個模式解決我們的特定問題,下圖或許能給你一點啟發:
?
Fig 3 - Solving Our First Design Problem
當調用球的 SetBallPosition 函數設置一個新的位置時,它馬上調用類 Ball 中定義的 Notify 函數。Notify 函數迭代觀察者序列,并調用它們的 Update 函數。當 Update 函數被調用,觀察者就可以通過調用 FootBall 類的 GetBallPosition 函數來得到球的新的狀態位置。
各部分詳述如下:
Ball (Subject)
下面是類 Ball 的實現。
FootBall (ConcreteSubject)
下面是類 FootBall 的實現。
IObserver (Observer)
下面是類 IObserver的實現,它提供了具體的觀察者(Concrete Observers)的接口。
轉載于:https://www.cnblogs.com/candybox/archive/2010/08/19/1803444.html
總結
以上是生活随笔為你收集整理的如何应用设计模式设计你的足球引擎的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Sun Solaris 10 bind
- 下一篇: 企业在管理系统方面要有主动权