亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

在線捉鬼游戲開發之二-設計業務對象與對象職責劃分(3)

2019-11-14 15:52:22
字體:
來源:轉載
供稿:網友

“回憶總是殘酷的”——在“設計業務對象與對象職責劃分(2)”中,對舊版本的代碼進行了剖析,也發現了不少臭味道,本篇將記錄我是如何建設新版的業務對象職責劃分。

一、復習設計模式

當初自學設計模式的路徑是:從《大話設計模式》開始(做了筆記),到Gof的《設計模式》,再到辛勤網友們的各篇總結日志(只看C#的可能會有些局限~)。此后,每當我有需要更新代碼的時候,或者覺得不太記得清23種經典設計模式的時候,我就會回翻我的筆記,主要看:模式目的、應用場景,以最快速度在腦子里回放。在復習的同時,會不自覺就想到這個設計模式可能可以解決要更新項目中的某些問題,然后立即翻看第(2)篇中的代碼剖析,并在草稿板(買了塊白板當草稿紙)上做uml類圖簡單設計,并在腦海里過示例代碼。我覺得這個做法就好像、也應該像日后需要熟能生巧的動作一樣,要不斷重復,直到嫻熟。

寫到這里的時候,覺得有必要分享一份筆記中一句話總結23中設計模式的部分,所以寫了:一句話的設計模式,相信必是槽點滿滿:)

經過復習,初步覺得也許能用上的模式有:

創建型:單例(Setting),候選:工廠;

結構型:裝飾(處理各角色間的結構關系),候選:橋接、代理;

行為型:觀察者(定時更新)、中介者(由Game作為各參與者的中介)、狀態(投死狀態改變,可能會設計過度),候選:訪問者;

 

二、優化游戲流程

(1)舊版:進入后有旁觀/報名按鈕供登陸者選擇,Table類負責維護所有人的名單列表,退出旁觀/報名都會不斷地變更類;

優化:進入后旁觀者什么也不需要做,而是報名者需要點擊“入座”按鈕——較符合現實場景;Table類不再維護旁觀者列表——因為Table游戲桌只需要管誰參與游戲,旁觀者叫什么名字在整個游戲流程中并沒有貢獻;游戲開始前的站起/入座(也就是報名與旁觀)都不會有新的類生成——因為具體是平民、白癡還是鬼的身份應該在游戲開始后才生成,而不是這一開始就確定下來,如此做法,也避免了(2)篇中類之間復雜轉換的問題。

(2)舊版:投票沒做完

優化:由AddBallot()增加投票—IsVoteEnd()是否投票結束——Roll()唱票,三個部分。即:投票環節時,每當有一位參與者點選了要投的人或棄權票,投票管理者都會檢查是否已經結束,若沒結束,則繼續接收投票,若已結束,就開始唱票。很好理解吧~

 

三、劃分對象職責與對象協作順序

想必這是本篇、也是本系列最重要的一節:在后續代碼實現過程中將不斷回顧此節內容,甚至發現此節可進一步優化之處,并更新之。

此節以UML2.0作為表示法,使用Rational Rose工具,從類圖、順序圖角度描述了關鍵業務類之間的關系,以及這些類在游戲主流程中是如何分發消息、互相協作完成任務的。

(1)首先看類圖

 

Class Diagram

不算復雜吧,其中還省略了部分關聯關系(如Ghost可直接與SpeakManager、VoteManager關聯,解決一開始的鬼內部討論首輪發言順序的問題),只顯示主要關系,我們先從舊版中沿用的類開始說:

舊版的7個類:Table、Game、Subject、Setting、Audience、Civilian、Ghost,除了Audience外,其他都沿用寫來了。

按照游戲順序來吧:

Table:是游戲程序一啟動就會存在的由單例模式創建的游戲桌類,內部維護了一個Game類與PlayerManager類。

為什么沒有方法呢?當然是有的,只是現在屬于初步設計階段,如前述提到的,這些類圖、順序圖,甚至游戲流程都可能在后續代碼實現中發現不合理之處,到時候再回頭修改(我也會回到相應文章進行修改,專門寫一篇此系列的日志——用于記錄關鍵的修改行為)。

PlayerManager:舊版Table類的臭味道之一就是職責過多,不但負責了通知Game類開始游戲,還負責維護桌面人數、核查是否開始,此處就將這些不必要的職責進行了分離,新建了專門維護人數的PlayerManager類,可見其方法是對Player進行Set、Delete與Get。關鍵屬性是維護了一個9個成員的String數組,NameArray,負責游戲開始前入座者名單的確定,以便列出順序。游戲開始并分配角色后,轉而維護9個成員的Player數組,PlayerArray。為什么用數組Array而不是舊版中的列表List——因為本來就定好了標配人數(可在xml中維護,此處以標配來說),只等對應入座,且所需內存更少,類之間傳遞的運算速度更快,便于存儲。

Setting:與舊版唯一不同的是,由Setting來負責檢查入座報名參加的人是否已經滿了IsFull(),以此返回布爾值類型,再一路向上匯報回到Table,由Table向Game發起開始游戲Start()的通知。

Game:原來的一大堆職責都分配出去了,無事一身輕了吧~嘿嘿!的確,分配出職責之后就只剩下開始Start(),與重新開始Restart()——結束一場游戲又開啟新一場時用,當然其實也可以由Table負責重新創建一個Game對象,那么Game就更輕松了~現在活脫脫一位嘴巴叼著煙斗的看門兒大爺,樂呵呵看著下邊兒小弟干活,自己只管發號開工、收工的指令,喔對了,連收工都不用說,底下小弟自己會判斷游戲是否結束。

Subject:與舊版唯一不同的是,由Subject來負責向詞庫獲取題目GetSubject(),并填充本身維護的三個存放詞的屬性,以后外界要詞,就都找他(舊版中做法是復制了一份給到各參與者手中,新的做法看似與現實不符,但實際上是優化現實中可能存在手動改題作弊的信息化流程的優勢),所以需要建立全局訪問點,故還是考慮單例模式創建。

Player、Civilian、Idiot、Ghost:后三者繼承Player,眼疾手快的應該看出來了——PlayerManager維護的Player數組是對抽象類Player,此后若需要從中提出指定的一類對象,就可考慮用lambda表達式完成了,如:List<Ghost> ghostList = PlayerManager.Player.ToList().Where(g=>g.Type().Equals(Ghost)); ——直接打的沒在vs運行過,有bug請多包涵哈[憨笑]!在舊版當中,Ghost繼承自Civilian,Civilian繼承自Audience,此處不考慮旁觀者類(在代碼實現部分在考慮補充,也許還會增加回來這個Audience類,就看增在哪了),且Ghost實際上不是一個Civilian,所以不應使用繼承關系表示,所有他們相似的方法,應該提升到Player抽象類來完成,如設計模式中提及的:非is-a關系的,不應該用繼承關系表示。那么如何區分Civilian平民、Idiot白癡、Ghost鬼呢——別忘了裝飾模式能夠動態給所包裝的對象額外添加職責或標簽喔~(回顧本篇第一節復習設計模式后,列出的可能用得上的設計模式部分)

好了,剩下沒講到的類,都是從舊版Game中分離出來的職責:

RoleManager負責分配角色、SpeakManager負責管理對話列表、LoopManager負責檢查此輪發言(包括PK時的發言)是否結束、VoteManager負責管理投票環節、DeathManager負責充當劊子手,WinManager負責檢查鬼是否勝利(鬼沒勝利就繼續,直到鬼全死或者鬼勝利,所以無需以好人們勝利作為結束游戲的標準)。

好了,如此一來各個類都比較專一的負責自己要做的事情了——單一職責原則——如果現實生活中真有這樣單一職責的工作該多好,而偏偏社會需要的是全才,就好比學計算機的也會被領導叫去搬主機箱一樣(比喻,比喻~),關于工作的話題小生我也就4年工作經驗,不敢在各位老江湖面前班門弄斧、講些閱歷少見識短的大道理,還是趁年輕、趁著夢想沒被現實叫醒,趕緊做能承受的起的事情吧~

(2)進入與游戲開始順序圖

Enter Diagram

順序圖是個啥東西,此處相信不認識的朋友們也能聽懂一二。

1-4. 每當有人入座,Table就會將此人昵稱丟給PlayerManager,由PlayerManager問Setting“人齊了沒?”(IsFull())。有人起立的時也一樣告訴PlayerManager要刪掉人名,此時PlayerManager維護的是String數組。若Setting告訴PlayerManager“人齊啦!”,消息會傳回Table的耳朵里,Table拔出一根猴毛那么一吹啊(作為繪畫愛好者,一定要向燃起的國產動畫《大圣歸來》致敬?。捅某鰝€Game類,游戲就此開始。

5-6. Game先向Subject要題,再叫來RoleManager隨機給PlayerManager維護的String數組分配角色,并將角色依次披上Player抽象類外衣,再列隊回到PlayerManager中。是不是感覺RoleManager有點像建造者模式中的指揮者——大老遠敢來只帶一身才華(類的方法),匆忙按要求完成任務后也不帶走一片云彩。哈哈,是有這么個意思,具體能否結合建造者模式、是否有必要結合建造者模式,我們在代碼實現部分與大家共同探討。

7-8. 題目和身份角色都準備好了,就由系統說話,向Player參賽者們說明他們各自的身份與題目,并說明現在進入鬼指定首輪發言人的時候。SpeakManager當之無愧作為此任務的完成者,顯示記錄下系統說了什么SystemSpeak(),再將記錄發布到前臺ShowRecord()。

(3)鬼討論順序圖

Ghost Speak Diagram

非常簡單,找SpeakManager就夠了~

(4)鬼投票(決定首輪發言人)順序圖

Ghost Vote Diagram

1-3. 在鬼投票決定首輪發言人的時候,前臺界面會出現所有人名字的按鈕(PlayerManager維護的String人名數組又派上用場了吧),每當有一個鬼點選投票的時候,Ghost對象將票遞給檢票官VoteManager,由檢票官查看大家都投完了嗎IsVoteEnd(),投完了咱就唱票Roll()。

4-5. 屬于備選事件流(來自RUP的提法,事件流分為主事件流與備選事件流),在鬼投票結果不一致(有人按錯,或意見不統一)時發生,此時檢票官VoteManager會讓SpeakManager幫忙發布圣旨SystemSpeak(),告訴前臺ShowRecord(),讓鬼們趕緊統一意見并再次投票。

6-7. 當鬼的投票一致時,檢票官可算松了口氣,趕緊把接力棒交給LoopManager,讓其記錄本輪開始,并讓SpeakManager來設定允許發言的鬼投出來的首輪發言人。什么叫“允許發言的”——如果非SpeakManager官方允許SetSpeaker()的玩家,無論他們說什么都不予記錄在案,當然也就不會ShowRecord()給別人看,也就是“不許場外”,這個“允許誰說話”的職責,肯定要落到SpeakManager的身上。

(5)所有角色玩家發言順序圖

Player Speak Diagram

與鬼發言不同的是,玩家發言要按順序,且每人發言后LoopManager都會不厭其煩的看一眼本輪是否結束。具體過程我就不贅述了。

(6)所有角色玩家投票順序圖

Player Vote Diagram

1-5. 正常投票,不贅述。

6-7. 屬于備選事件流,用于投票結果出現相同票數時的PK發言環節。顯示投票官將同票者們交給LoopManager,讓其決定誰先說SetLoopStarter(),別忘了要告訴SpeakManager允許記錄他的話SetSpeaker(),說完后就會回到1-5步驟,繼續投票。當然,如系列中的游戲流程介紹篇所言,PK臺上的人是不能進行他們自己的投票的,這一點在順序圖中不體現,在實際代碼中可以通過Player的是否有投票權屬性來標識(暫時性剝奪投票權利法治制度的趕腳哈哈)。

8-9. 無論如何,每輪結束肯定得有人被檢票官VoteManager交給死神DeathManager,并由死神執行死刑SetPlayerDie()——舊版中是玩家自殺(SetDie的方法在玩家對象中),新版是劊子手處決,感覺更合理了吧~每次有人升天,WinManager大仙都會探出頭看看凡間這桌的這個游戲(Table.Game)是否結束,結束的標準是鬼是否勝利IsGhostWin()。

10-11. 如果游戲還沒結束,那么游戲的指揮棒再次回到VoteManager手中(畢竟整個都是投票環節產生的一系列互動,讓其他任何Manager接棒都不合適),檢票官會通知LoopManager開始新的一輪玩家發言,當然別忘了經過SpeakManager允許的SetSpeaker(),才能言論自由喔~

 

四、總結此篇步驟

本篇末尾特此增加一節,來講述上述那一堆(我自認為還算是比較)清晰的類職責劃分與流程,是如何在舊版思維潛移默化根植的情況下建立出來的。先上圖:

為了讓大家看清筆記,我就不縮略了。

這是我設計時考慮的第一張圖,可以看到左上角,當時還是沿用的三層繼承的方式處理各個角色,因為一時沒想通,就不希望在此環節卡殼,就繼續了對Table和Game的職責進行分離??醋笙陆牵畛醯腜layerManager還只是一個孩子(PlayerList,一個屬性而已),針對的是繼承于Audience的Player抽象類(但仍然沒解決三層繼承的問題,且Audience思維根深蒂固)。在看到圖中間Game分出來的職責,重新招聘新員工后,將這些職責分配了他們,他們最初的名字還是Roler、Speaker、LoopChecker、Voter、WinChecker、Death、StartChecker(最后通過職責分析,更應該被分到Setting中,才有了如今花名IsFull()的方法,從此遠離塵世,隱退江湖&hellip;…)。各個類之間的大體順序可在圖中右側1-13的序號查看,眼尖的朋友可能看出了判斷Y/N邏輯,哈哈~

這第二幅,是為了專門解決各角色間的關系問題所畫的??梢钥吹?,這時候已經誕生了PlayerManager類,并和Game類平起平坐被Table大哥掌管。圖中凌亂不堪的筆記是雨落邕城的日子里思緒糾纏的痕跡,細心的讀者也許看到了枚舉Role:Enum,是的,當時我是想Player類繼承自Audience類,這樣就化三層繼承為兩層繼承,所有角色以枚舉類型的Role屬性來區分,但此時因考慮到開放封閉原則(為擴展開放,為修改封閉),不適用于枚舉的增加——如:如果有一天游戲升級,規則中加入了華佗角色,可以起死復生(有點像殺人游戲的升級版),難道還要進入枚舉中進行修改嗎?為何不通過增加一個新類的方式來解決呢,因此就有了圖中Person類的框框,其他角色繼承自Person——沒錯,那這么一來不就又回到舊版本的三層繼承?不,雖然多層繼承問題還沒解決,但至少解決了一個重要問題:非is-a關系不能用繼承——Civilian與Ghost不再是繼承關系了!這點太重要了。請大家記住,如果只是為了方法調用方便,完全可以通過模板模式解決,再不行代理、外觀模式也成啊,反正繼承關系真是不到“是一個”(is-a)關系的時候,就不要考慮,否則可能出現龐大的繼承樹問題,或者像舊版一樣陷入父子類頻繁轉換漩渦當中,也不利于對繼承樹中的某個環節進行額外增加職責或標簽(方法或屬性)。

觀察力強的朋友們也許注意到了右上角圍著桌子做的玩家座位圖。沒錯,當時是覺得前臺界面不但要略微優化,還要解決Audience類的問題,就萌生了類似德州撲克那樣“圓桌會議+坐下按鈕”的方式,以此代替原來報名/旁觀的按鈕。拜德州撲克所賜,忽然間覺得Audience這類人真的對游戲貢獻很不大。好像你去澳門賭場,你也許只關心同桌競技的對手是何人是何心理,而一點不關心圍桌站起的旁觀者(不要腦補賭神的作弊旁觀者啊~),甚至連旁觀者的名字都不想知道,頂多知道圍著大概多少人就行了(通過cookie統計數字即可,且不需要即時更新,定較長的時間更新都不影響,還可節省流量)。

因此我認為界面應該重新稍微布局一下,順便理順第一張草稿圖中的主要順序,因此有了第三張圖:

哈哈,第一眼都看圖去了吧,好像除了列出圓桌也沒啥區別,好吧……我承認的確是的。

圖中右側形如大括號、箭頭的,就是順序圖中的信息流箭頭。是不是發現很粗糙,甚至整個流程(順序圖)那么多,怎么幾行就搞定了:畫到第三張圖時,我覺得混沌的思路已經打開,職責劃分、流程優化、對象關系等主要問題已經基本迎刃而解,只剩下規規整整的列出一份能見世面的圖紙罷了——即,草圖整理思路的環節已告破,可進入匯總思路、整理文檔的環節,進而我就轉入了上述第三節類圖、順序圖的繪制過程。

如果你一定要問上述三張圖都是什么表示法,那我只能拍腦袋隨便起個毫無意義的名兒了——不要局限于手中的繪圖工具(rational rose或visio或vs的modeling項目),一開始是無法對著如此工整的電腦軟件將大腦中思維跳躍、混亂待整的腦電波表現出來的,個人建議還是在草稿紙上進行,框框線線、粗糙標記,以最快速度記下所想所悟,別忘了,軟件再高級也是為人類服務的,相信自己的大腦與握筆的手吧!

如果一定要說順序,那請參照RUP(統一軟件開發過程),多了解OOAD(面向對象分析與設計),結合SOLID原則(單一職責、開放封閉、里氏替換、接口隔離、依賴倒轉)在整個設計、代碼編寫過程不斷迭代審視,最終做到perfect——不禁想起我的一位高齡素描老師,趙晉,趙老師熱愛畫油畫,有一副描繪了大榕樹下的農民生活的油畫他花了很多年,今天釣魚回來添幾筆,明年大年初一高興又添幾筆,如此反復……

Coder們加油,我們要做的事情還有很多,即便不在技術的道路上走,也能交交朋友,從代碼中看到態度、領悟世事,不要枉費曾在IT之路走了這一遭。

PS:也許會有讀者疑問,怎么設計的結果沒體現具體設計模式?因筆者考慮到,設計模式不能離開代碼,且設計模式是思路、是建議,而非終極目標,能在設計環節考慮到、思考到,待到代碼實現環節再寫出其內涵與精髓,甚至模式變形,也比陷入設計過度要好。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品久久婷婷六月丁香| 91中文精品字幕在线视频| 国产脚交av在线一区二区| 国产精品美女www爽爽爽视频| 亚洲社区在线观看| 一本大道亚洲视频| 欧美区在线播放| 日韩av电影在线免费播放| 成人免费在线网址| 久久男人资源视频| 日韩精品在线免费观看| 色综合视频网站| 国产91av在线| 国产免费一区视频观看免费| 久久中文精品视频| 国产精品爽爽ⅴa在线观看| 亚洲精品久久久久久久久| 欧美性xxxxhd| 欧美片一区二区三区| 91免费的视频在线播放| 亚洲国产美女久久久久| 日韩av成人在线观看| 亚洲精品永久免费精品| 欧美日韩中文字幕在线| 人人爽久久涩噜噜噜网站| 69**夜色精品国产69乱| 日韩日本欧美亚洲| 欧美二区在线播放| 国产欧美 在线欧美| 日本精品在线视频| 亚洲第一精品自拍| 97超级碰碰人国产在线观看| 国产精品v片在线观看不卡| 日韩电影免费在线观看| 久久伊人91精品综合网站| www.日韩系列| 国产精品老女人视频| 国产精品网址在线| 成人久久一区二区三区| 亚洲影影院av| 青草成人免费视频| 久久久久在线观看| 欧美日韩中文字幕日韩欧美| 日韩中文字幕视频在线观看| 亚洲成人激情在线| www国产亚洲精品久久网站| 日本欧美一二三区| 精品毛片网大全| 日韩美女视频在线观看| 欧美亚洲国产日韩2020| 综合欧美国产视频二区| 国产做受69高潮| 成人黄色短视频在线观看| 欧美精品免费在线| 成人午夜在线观看| 色先锋久久影院av| 欧美日韩999| 92福利视频午夜1000合集在线观看| 精品亚洲精品福利线在观看| 国产精品免费观看在线| 国产精品视频专区| 欧美成年人视频网站| 亚洲成年人在线播放| 亚洲国产日韩欧美在线图片| 岛国av一区二区三区| 日韩av影视综合网| 欧美美最猛性xxxxxx| 精品国产91久久久久久老师| 欧美亚洲国产视频| 92看片淫黄大片欧美看国产片| 国产欧美日韩综合精品| 亚洲天堂一区二区三区| 91热福利电影| 日韩电视剧免费观看网站| 日韩一级黄色av| 日韩在线观看免费全集电视剧网站| 国产精品久久久久久久av电影| 91av在线国产| 国产精品久久久久久av福利软件| 国产91成人video| 中文字幕日韩精品在线观看| 91伊人影院在线播放| 日韩高清人体午夜| 欧美日韩一区二区免费视频| 欧美国产日韩中文字幕在线| 亚洲bt欧美bt日本bt| 亚洲一区二区三区在线免费观看| 一本大道亚洲视频| 91成人免费观看网站| 亚洲精品99久久久久中文字幕| 国产成人福利网站| 久久久久久久一区二区| 69av在线视频| 成人黄色免费在线观看| 亚洲欧美国产另类| 国产视频久久久| 日韩黄在线观看| 一区二区三欧美| 欧美日韩在线第一页| 国产精品久久久亚洲| 国产精品久在线观看| 波霸ol色综合久久| 成年人精品视频| 久久国产精品久久国产精品| 69影院欧美专区视频| 日韩经典中文字幕在线观看| 亚洲欧美精品伊人久久| 日韩精品中文字幕久久臀| 91精品国产精品| 欧美日韩久久久久| 国产精品网红直播| 久久久久久久一区二区| 中文字幕亚洲自拍| 亚洲国产私拍精品国模在线观看| 欧美区二区三区| 色综合91久久精品中文字幕| 国产精品视频99| 最好看的2019年中文视频| 久久精品人人做人人爽| 国产区精品在线观看| 欧美激情视频在线免费观看 欧美视频免费一| 亚洲人成免费电影| 中文字幕日韩视频| www.久久撸.com| 日韩中文字幕国产精品| 欧美日韩午夜激情| 国产精品成久久久久三级| 中日韩美女免费视频网址在线观看| 欧美高清第一页| 国产精品88a∨| 久久久精品国产一区二区| 午夜精品久久久久久久99热浪潮| 欧美精品在线免费观看| 亚洲字幕一区二区| www.欧美三级电影.com| 68精品久久久久久欧美| 亚洲国产欧美自拍| 日韩在线视频导航| 亚洲一区二区少妇| 日韩在线欧美在线国产在线| 亚洲激情视频网站| 成人午夜两性视频| 欧美亚州一区二区三区| 国内精品一区二区三区| 精品爽片免费看久久| 亚洲成人av片| 国产精品偷伦视频免费观看国产| 亚洲xxxxx| 久久久久国产精品免费| 精品一区二区亚洲| 日韩激情av在线免费观看| 亚洲一区二区精品| 国产热re99久久6国产精品| 久久久精品在线观看| 亚洲人成伊人成综合网久久久| 国产精品福利网站| 日韩免费观看在线观看| 日韩美女写真福利在线观看| 68精品国产免费久久久久久婷婷| 午夜精品三级视频福利| 欧洲一区二区视频| www.xxxx精品| 91极品女神在线| 久久99精品久久久久久噜噜|