麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

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

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

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

來咯!因為章節較多,先做小部分回顧,熟悉的朋友就直接跳過吧。

-----------回顧分割線-----------

此系列旨在開發類似“誰是臥底+殺人游戲”的捉鬼游戲在線版,記錄從分析游戲開始的開發全過程,通過此項目讓自己熟悉面向對象的SOLID原則,提高對設計模式、重構的理解。

系列索引點這里,游戲整體流程介紹與技術選型點這里,之前做了一半的版本點這里(圖文)

在設計業務對象與對象職責劃分(1)中對之前做的版本進行了截圖說明部分views的細節,相信大家已對游戲流程更熟悉了(我也回顧了一遍,包括游戲技巧很有意思~),本第(2)篇主講MVC的M和C,深入剖析游戲業務在代碼中的體現。如索引篇所說,大家的技術性吐槽與質疑是對其他讀者和我最大的幫助;如上一篇所言,這個做了一半的版本壞味道很多,尤其從設計模式的角度看就更明顯了(這個系列做完,我計劃再寫一個俄羅斯方塊系列,俄羅斯方塊是已經做好的C#+WinForm,但同樣是壞味道嚴重,拓展不易,也才更需要update),所以才有了重寫一遍捉鬼游戲,并想把整個項目建設過程同步更新到博客園,以給和我一樣的初學者一個共同思考和進步的途徑。我始終相信,知其然還要知其所以然,甚至和項目開發者同步思考過、犯過錯、再一起探討與改正過,會獲得更深刻的經驗。也歡迎朋友們勇敢的在最后把代碼直接拿去說這是我和一位博客園朋友一期開發的小項目![自豪] 筆者對“開源”一詞的理解還不太深刻,但我相信無償的提供代碼、寫代碼的心得,甚至寫代碼的全程思考會讓大家比直接拿商用代碼更有價值。

-----------回顧結束分割線-----------

-----------本篇開始分割線-----------

1. Models

業務類目錄

從類目錄可看出一共7個類,先從最輔助性的英雄(啊不是英雄,是類)Setting開始(已在上篇貼過代碼)

(1)Setting(設置類)負責從Web.config獲取游戲人數的設定(標配9人,開發測試3人),Setting作為全局訪問點采用了singleton單例模式。

    public class Setting    {        PRivate int _civilianCount;        private int _GhostCount;        private int _idioCount;        public int IdioCount        {            get { return _idioCount; }        }        public int GhostCount        {            get { return _ghostCount; }        }        public int CivilianCount        {            get { return _civilianCount; }        }        public int GetTotalCount()        {            return IdioCount + GhostCount + CivilianCount;        }        // singleton        private static Setting _instance;        private Setting()        {            this._civilianCount = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["CivilianCount"]);            this._ghostCount = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["GhostCount"]);            this._idioCount = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["IdioCount"]);        }        public static Setting CreateInstance()        {            if (_instance == null)            {                _instance = new Setting();            }            return _instance;        }    }
Setting

 (2)Subject(題目類)負責從題庫出題(題庫系統未做,要做也是普通的增刪改查即可,先暫寫死在程序里),也是單例模式。

    public class Subject    {        // singleton        private static Subject _instance;        private Subject()        {            // Todo: get subject from dictionary            this._civilianWord = "周星馳";            this._idiotWord = "孫悟空";        }        public static Subject CreateInstance()        {            if (_instance == null)            {                _instance = new Subject();            }            return _instance;        }        private string _civilianWord;        private string _idiotWord;                public string IdioWord        {            get { return _idiotWord; }        }        public string CivilianWord        {            get { return _civilianWord; }        }    }
Subject

(3)Table(游戲桌類)負責數桌上的人數以及開始或重開游戲【壞味道:職責過多】

代碼過多,可先從貼圖看起

Table游戲桌類

先看屬性:可見也是單例模式(_instance),且存在與Game類的關聯關系(_game),以及維護有一個audiences列表(_audiences,List<Audience>類型)

再看方法:先看第二個CheckTotalNum()——處理每次有人點擊報名后,都核查一遍報名總人數是否與Setting設置類中的游戲人數相等,若想等則調用GameStart()方法。其他方法都是Add或Get開頭的,想必看方法名就能理解了:都是獲取所維護的_audiences列表中各個類型的角色的集合,舉個帶Without的GetAudiencesWithoutCivilians來說,就是獲取除了Civilian子類中的其他Audiences,也就是旁聽的有幾人。

壞味道很明顯:職責過多。(注意不是方法過多,而是類負責的職責過多。區別是:可能存在只有一個職責,但需要多個private私有方法來配合完成,這也是允許的)

代碼還是貼一下吧,助于具體方法的理解。

    public class Table    {        // singleton        private static Table _instance;        private Table() { }        public static Table CreateInstance()        {            if (_instance == null)            {                _instance = new Table();            }            return _instance;        }        // field        private List<Audience> _audiences = new List<Audience>();        private Game _game;        public Game GetGame()        {            return this._game;        }        public void AddAudience(Audience audience)        {            if (!this.GetAudiences().Contains(audience))            {                if (audience is Civilian)                {                    this.GetAudiences().Add(audience as Civilian);                    CheckTotalNum();                    return;                }                this.GetAudiences().Add(audience);            }        }        public void RemoveAudience(Audience audience)        {            this._audiences.Remove(audience);        }        private void CheckTotalNum()        {            if (this.GetCivilians().Count >= Setting.CreateInstance().GetTotalCount())            {                GameStart();            }        }        // 獲取在線者        public List<Audience> GetAudiences()        {            return this._audiences;        }        // 獲取聽眾        public List<Audience> GetAudiencesWithoutCivilians()        {            List<Audience> result = new List<Audience>();            foreach (Audience a in this._audiences)            {                if (!(a is Civilian))                {                    result.Add(a);                }            }            return result;        }        // 獲取參與者        public List<Civilian> GetCivilians()        {            List<Civilian> result = new List<Civilian>();            foreach (Audience a in this._audiences)            {                if (a is Civilian)                {                    result.Add(a as Civilian);                }            }            return result;        }        // 獲取好人        public List<Civilian> GetCiviliansWithoutGhosts()        {            List<Civilian> result = new List<Civilian>();            foreach (Civilian a in this.GetCivilians())            {                if (!(a is Ghost))                {                    result.Add(a);                }            }            return result;        }        // 獲取鬼        public List<Ghost> GetGhosts()        {            List<Ghost> result = new List<Ghost>();            foreach (Audience a in this._audiences)            {                if (a is Ghost)                {                    result.Add(a as Ghost);                }            }            return result;        }        // game start        private void GameStart()        {            this._game = new Game();            this.GetGame().Start();        }        // restart        public void Restart()        {            _instance = null;        }    }
Table

 (4)Audience(聽眾類)、Civilian(好人類)、Ghost(鬼類)存在繼承關系

三個參與者身份類之間的繼承

    public class Audience    {        private string _nickname;        public string Nickname        {            get { return _nickname; }        }        public Audience(string nickname)        {            this._nickname = nickname;        }        private Table table;        public Table Table        {            get { return table; }            set { table = value; }        }        public static Audience CreateFromCivilian(Civilian civilian)        {            return new Audience(civilian.Nickname) { Table = civilian.Table };        }    }
Audience
    public class Civilian : Audience    {        private string _word;        public string Word        {            get { return _word; }            set { _word = value; }        }        private bool _isAlive = true;        public bool IsAlive        {            get { return _isAlive; }        }        public void SetDeath()        {            this._isAlive = false;        }        public Civilian(string nickname) : base(nickname) { }        public static Civilian CreateFromAudience(Audience audience)        {            return new Civilian(audience.Nickname) { Table = audience.Table };        }        public string Speak(string speak)        {            return Table.CreateInstance().GetGame().RecordSpeak(speak.Replace("/r/n", " "), this);        }    }
Civilian
    public class Ghost : Civilian    {        public Ghost(string nickname) : base(nickname) { }        public static Ghost CreateFromCivilian(Civilian civilian)        {            return new Ghost(civilian.Nickname);        }    }
Ghost

Audience聽眾類:任何輸入昵稱(Audience聽眾類的nickname屬性)的用戶都會是Audience身份,Audience不能發言、看詞、投票,只能看到發言內容的聽眾。細心的朋友一定發現了此類與Table游戲桌類的關聯關系(所以關聯關系是:Audience--Table--Game),類中還有一個CreateFromCivilian(從好人轉為聽眾身份)的方法【壞味道:轉換混亂】——負責處理已報名參加但在游戲開始前又想退出報名的用戶。

Civilian好人類:繼承了聽眾類的昵稱屬性,外加聽眾類沒有的“詞語”、“是否活著”屬性,能做的動作有:被投死(SetDeath方法)、說話(Speak方法)、從聽眾轉為好人的方法(CreateFromAudience)【壞味道:轉換混亂】——在點擊報名按鈕后調用。

Ghost鬼類:唯一與好人類不同的是能從好人這個父類中轉為鬼類(CreateFromCivilian)【壞味道:轉換混亂】——在分配角色時,把隨機抽到要當鬼的好人身份轉化為鬼身份。

是不是很多壞味道了?YES,多的受不了了吧,首先Audience、Civilian、Ghost三個類之間的轉化方法就受不了,轉來轉去、毫無秩序、混論一通;其次怎么沒有Idiot白癡類(上面拼錯了Idio,英文丟人又現),卻在Setting設置類中有Idiot的人數記錄、在Subject題目類中有Idiot的詞語;再者Ghost和Civilian感覺就是一個標識的區別,甚至連標識屬性都沒有,只是換了一個類名;最后,好人類中出現的被投死方法不應在這里,因為自己是不能決定自己被投死的,應該由投票結果決定,而且這么做也顯得好人的職責過多了。

沒錯,在上一篇看似還能玩的通的游戲背后,有那么多發臭的代碼令人厭惡,不怕,堅持下去,就像整理混亂的書桌一樣,要堅信最終的結果一定會帥自己一臉!

(5)Game(游戲類)

游戲類截圖長的我就不想看,肯定【壞味道:職責過多】

不信你看~

Game游戲類

長的連圖我都截不了...醉了醉了。

完整代碼建議就更不要看了,想copy的拿走。

  1     public class Game  2     {  3         private Table table = Table.CreateInstance();  4   5         // start  6         public void Start()  7         {  8             SetGhostWithRandom();  9             SetWordWithRandom(); 10             AppendLineToInter("鬼正在指定開始發言的順序..."); 11             AppendLineToGhostInter("已開啟鬼內討論,此時的發言只有鬼能看見..."); 12             AppendLineToGhostInter("請指定首發言者..."); 13         } 14  15         private StringBuilder _inter = new StringBuilder(); 16         private StringBuilder _ghostInter = new StringBuilder(); 17         private bool _isGhostSpeaking = true; 18         private bool _isVoting = false; 19         private Civilian _speaker; 20         private Civilian _loopStarter; 21         private bool _isFirstLoop = true; 22  23         public void SetStartSpeaker(Ghost ghost, Civilian speaker) 24         { 25             this._speaker = speaker; 26             this._loopStarter = speaker; 27             SetGhostSpeaked(); 28             AppendLineToGhostInter(string.Format("{0} 選擇了 {1} 作為首發言人", ghost.Nickname, speaker.Nickname)); 29         } 30         private void SetGhostSpeaked() 31         { 32             if (isGhostSpeaking()) 33             { 34                 this._isGhostSpeaking = false; 35                 StartLooping(); 36             } 37         } 38  39         private void StartLooping() 40         { 41             ClearInter(); 42             AppendLineToInter("" + this.GetCurrentSpeaker().Nickname + " 開始發言..."); 43         } 44  45         public void SetNextSpeaker() 46         { 47             List<Civilian> list = this.GetAliveCivilian(); 48             for (int i = 0; i < list.Count; i++) 49             { 50                 Civilian current = list[i]; 51                 if (current == this.GetCurrentSpeaker()) 52                 { 53                     if (i == list.Count - 1) 54                     { 55                         this._speaker = list[0]; 56                         CheckEndLoop(); 57                         return; 58                     } 59                     this._speaker = list[i + 1]; 60                     CheckEndLoop(); 61                     return; 62                 } 63             } 64         } 65  66         private void CheckEndLoop() 67         { 68             if (GetCurrentSpeaker() == this._loopStarter) 69                 EndLoop(); 70         } 71  72         private void EndLoop() 73         { 74             Thread.Sleep(30000); 75             if (this._isFirstLoop) 76             { 77                 this._isFirstLoop = false; 78                 AppendLineToInter("第二輪開始"); 79                 return; 80             } 81             AppendLineToInter("30秒后開始投票"); 82             ClearInter(); 83             SetVoting(); 84         } 85  86         public Civilian GetCurrentSpeaker() 87         { 88             return this._speaker; 89         } 90  91         public bool isGhostSpeaking() 92         { 93             return this._isGhostSpeaking; 94         } 95  96         public bool isVoting() 97         { 98             return this._isVoting; 99         }100 101         private void SetVoting()102         {103             this._isVoting = true;104             AppendLineToInter("開始投票");105         }106 107         private void SetVoted()108         {109             this._isVoting = false;110         }111 112         public string RecordSpeak(string speak, Civilian civilian)113         {114             if (isGhostSpeaking())115             {116                 if (civilian is Ghost)117                 {118                     AppendLineToGhostInter(FormatSpeak(civilian.Nickname, speak));119                     return "ok";120                 }121                 return "天黑請閉眼!";122             }123             AppendLineToInter(FormatSpeak(civilian.Nickname, speak));124             SetNextSpeaker();125             return "ok";126         }127 128         private string FormatSpeak(string name, string speak)129         {130             return string.Format("【{0}】:{1}", name, speak);131         }132 133         public List<Civilian> GetAliveCivilian()134         {135             return Table.CreateInstance().GetCivilians().Where(c => c.IsAlive).ToList();136         }137 138         public string GetGhostInter()139         {140             return this._ghostInter.ToString();141         }142 143         public string GetInter()144         {145             return this._inter.ToString();146         }147 148         private void AppendLineToInter(string line)149         {150             this._inter.AppendLine(line);151         }152         private void AppendLineToGhostInter(string line)153         {154             this._ghostInter.AppendLine(line);155         }156 157         private void ClearInter()158         {159             this._inter.Clear();160             this._ghostInter.Clear();161         }162 163         private void SetGhostWithRandom()164         {165             Random rd = new Random();166             while (true)167             {168                 for (int i = 0; i < Setting.CreateInstance().GetTotalCount(); i++)169                 {170                     Civilian c = table.GetCivilians()[i];171                     if (c is Ghost) continue;172                     int role = rd.Next(1, 4); // 1/3幾率173                     if (role == 1 && table.GetGhosts().Count < Setting.CreateInstance().GhostCount)174                     {175                         Ghost ghost = Ghost.CreateFromCivilian(c);176                         table.RemoveAudience(c);177                         table.GetAudiences().Insert(i, ghost);178                     }179                 }180 181                 if (table.GetGhosts().Count >= Setting.CreateInstance().GhostCount)182                     break;183             }184         }185         // set word186         private void SetWordWithRandom()187         {188             Random rd = new Random();189             foreach (Civilian c in table.GetCivilians())190             {191                 if (c is Ghost)192                 {193                     SetGhostWord(c as Ghost);194                     continue;195                 }196 197                 int role = rd.Next(4, 10);198                 if (role <= 7)199                 {200                     // 4,5,6,7201                     SetCivilianWord(c);202                 }203                 else204                 {205                     // 8,9206                     SetIdioWord(c);207                 }208             }209         }210         private void SetGhostWord(Ghost g)211         {212             if (HasWord(g)) return;213             g.Word = string.Format("鬼({0}字)", Subject.CreateInstance().CivilianWord.Length);214         }215         private void SetCivilianWord(Civilian civilian)216         {217             if (HasWord(civilian)) return;218             string word = Subject.CreateInstance().CivilianWord;219             List<Civilian> list = table.GetCiviliansWithoutGhosts();220             if (list.Where(c => c.Word == word).Count() < Setting.CreateInstance().CivilianCount)221             {222                 civilian.Word = word;223                 return;224             }225             SetIdioWord(civilian);226         }227         private void SetIdioWord(Civilian civilian)228         {229             if (HasWord(civilian)) return;230             List<Civilian> list = table.GetCivilians();231             string word = Subject.CreateInstance().IdioWord;232             if (list.Where(c => c.Word == word).Count() < Setting.CreateInstance().IdioCount)233             {234                 civilian.Word = word;235                 return;236             }237             SetCivilianWord(civilian);238         }239         private bool HasWord(Civilian c)240         {241             return !string.IsNullOrEmpty(c.Word);242         }243     }
Game

按游戲順序理解Game的職責:

(1)負責隨機分配角色

SetGhostWithRandom()

(2)負責分配詞語

SetWordWithRandom(), SetGhostWord(), SetCivilianWord(), SetIdioWord(), HasWord():bool

(3)負責設置發言者

SetStartSpeaker(), SetNextSpeaker(), GetCurrentSpeaker()

(4)負責鬼討論

isGhostSpeaking():bool, SetGhostSpeaked()

(5)開始輪流發言、記錄發言

StartLooping(), RecordSpeak(), FormatSpeak(), GetInter(), GetGhostInter(), AppendLineToInter(), AppendLineToGhostInter()

(6)負責檢查此人發言后是否是本輪發言結束

CheckEndLoop(), EndLoop()

(7)負責投票

isVoting(), SetVoting(), SetVoted()

上述七個責任分配還不是很明確,且明顯職責多的都要爆炸了,必須在新版中分離。

 

2. Common:(較為簡單不贅述)

WebCommon負責獲取各種session

public static class WebCommon    {        public static Audience GetAudienceFromSession()        {            return HttpContext.Current.Session["player"] as Audience;        }        public static Civilian GetCivilianFromSession()        {            return HttpContext.Current.Session["player"] as Civilian;        }        public static Ghost GetGhostFromSession()        {            return HttpContext.Current.Session["player"] as Ghost;        }        public static void RenewPlayerSession(Audience newAudience)        {            HttpContext.Current.Session["player"] = newAudience;        }        public static void AddPlayerSession(Audience audience)        {            HttpContext.Current.Session.Add("player", audience);        }        public static void RemovePlayerSession()        {            HttpContext.Current.Session.Remove("player");        }    }
WebCommon

 AudienceFilterAttribute負責過濾聽眾,即區分玩家與旁觀者的操作許可和界面顯示內容

    public class AudienceFilterAttribute : ActionFilterAttribute    {        public override void OnActionExecuting(ActionExecutingContext filterContext)        {            HttpContextBase context = filterContext.HttpContext;            if (context.Session["player"] == null)            {                context.Response.Redirect("/");                return;            }            base.OnActionExecuting(filterContext);        }    }
AudienceFilterAttribute

過濾效果在Controller中調用

[AudienceFilter]public class PlayController : Controller{        //...}public class HomeController : Controller{        [HttpPost]        [AudienceFilter]        public ActionResult Logout(){//...}        [AudienceFilter]        public ActionResult Signout(){//...}        [AudienceFilter]        public ActionResult Restart(){//...}}
[AudienceFilter]

 

3. Controllers

(1)HomeController:負責登陸、登出,處理Session問題

HomeController

Logout():“退出報名”或“退出旁觀”,回到剛輸入昵稱進入桌子的界面。

Signout():“完全退出”登陸這個應用程序,回到輸入昵稱前的界面。

Restart():重新開始一局,保持當前的身份(報名/旁觀)

(2)PlayController:負責選擇“報名”或“旁觀”后的頁面

PlayController

其中,獲取參與者人數GetCivilians(), 獲取旁聽者人數GetAudiencesWithoutCivilians(), 獲取對話記錄GetInter(), 獲取游戲是否開始的狀態GetGameState(), 獲取投票區域GetVoteArea(),上述都是for eventsource——使用HTML5的服務器發送事件技術處理的,提供前臺頁面定時刷新的內容,當然,有的方法只在特定的時間獲取(如獲取游戲狀態,游戲開始后就不會再調此方法了)。

獲取詞的方法是GetWord(),采用Ajax完成(只調一次)。

剩下就是發言Speak()和投票Vote()方法了。

也許大家注意到了,為了用戶體驗稍好一點,除了用戶需要點擊屏幕操作的發言、投票兩個方法,其他都是用了異步獲取的方式來完成。

--------------OK,至此,整個項目詳細的代碼剖析已結束--------------

哪些代碼沒看懂的,或者哪個類的職責沒分清的,或者游戲流程還不清楚的,再或者需要詳細貼哪些代碼的,都可以留言給我,我都會詳細解釋,以便為新版的業務對象職責設計有更好的理解。

此外,若讀者還發現了我沒提及的壞味道問題,請一定一定留言給我,因為從下一篇開始,就是新項目的開始了,我也希望能改的更完善、更極致。因為本周比較忙,爭取一周內做好業務對象的優化——也就是對此篇中壞味道的優化設計。

新項目我會放在我的svn上,到Models代碼出來后再公布上來。(其實是我還沒熟悉Github和TSF,所以只能用最熟悉的svn了><,我會努力的~)

感謝閱讀!尤其是近幾日來一直跟隨此系列的朋友們,你們的閱讀量是我最大的動力、鼓勵與壓力,是的,如果沒有你們,如果我只是打開word寫給自己,那么我估計我也懶得再思考這么詳細了。

再次感謝!


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: arabxxxxvideos | 欧美淫视频 | 国产亚洲精品成人a | 日韩一级片毛片 | japanese javhd| 国产成人在线观看免费 | 欧美日韩国产综合网 | 久久精品视频69 | 福利在线小视频 | 依依成人综合 | 麻豆传传媒久久久爱 | 成人福利视频在线观看 | 亚州精品天堂中文字幕 | 成人精品久久 | 国产三级在线视频观看 | 中文字幕精品在线播放 | china对白普通话xxxx | 一级做a爱片久久 | 国产精品9191 | 欧美国产日韩在线观看成人 | 欧美日韩一区三区 | 国产一国产精品一级毛片 | 欧美性受xxxx人人本视频 | 精品黑人一区二区三区国语馆 | 在线观看免费毛片视频 | 欧美黄色一级片在线观看 | 国产精品99久久久久久董美香 | 手机免费看一级片 | 中文字幕在线观看视频一区 | 久草在线网址 | 中国美女一级黄色大片 | av电影院在线观看 | 欧美精品免费一区二区三区 | 激情小说激情电影 | 一区二区精品在线 | 精品国产高清一区二区三区 | 欧美成人精品不卡视频在线观看 | 污片视频网站 | 国产91免费看 | 欧美视屏一区二区 | 成人毛片100免费观看 |