您好,登錄后才能下訂單哦!
這篇文章主要介紹了C#中對象狀態模式怎么實現的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇C#中對象狀態模式怎么實現文章都會有所收獲,下面我們一起來看看吧。
首先用一個枚舉,表示教程進行的不同程度
enum TutorialState { GetGold, GetIron, KillEnemy, LevelUp }
無需多言,封裝收集到的資源數、擊殺敵人數量、角色等級和一些升級接口等
class Player { private int ironNum; private int goldNum; private int enemyKilled; private int level; public int IronNum => ironNum; public int GoldNum => goldNum; public int EnemyKilled => enemyKilled; public int Level => level; public void CollectIron(int num) { ironNum += num; } public void CollectGold(int num) { goldNum += num; } public void KillEnemy() { enemyKilled++; } public void LevelUp() { level++; } }
定義一個教程類,包括
顯示幫助文字以協助玩家通過當前教程步驟
判斷玩家是否已經完成當前教程步驟,若是,切換到下一個步驟直到完成教程
class GameTutorial { private TutorialState currentState; private Player player; public GameTutorial(Player player) { this.player = player; } public void ShowHelpDescription() { switch (currentState) { case TutorialState.GetGold: Console.WriteLine("Please follow instruction to get gold"); break; case TutorialState.GetIron: Console.WriteLine("Please follow instruction to get Iron"); break; case TutorialState.KillEnemy: Console.WriteLine("Please follow instruction to kill enemy"); break; case TutorialState.LevelUp: Console.WriteLine("Please follow instruction to Up your level"); break; default: throw new Exception("Not Support"); } } public void ValidateState() { switch (currentState) { case TutorialState.GetGold: { if (player.GoldNum > 0) { Console.WriteLine("Congratulations, you finished Gold Collect Phase"); currentState = TutorialState.GetIron; } else { Console.WriteLine("You need to collect gold"); } break; } case TutorialState.GetIron: { if (player.IronNum > 0) { Console.WriteLine("Congratulations, you finished Iron Collect Phase"); currentState = TutorialState.KillEnemy; } else { Console.WriteLine("You need to collect Iron"); } break; } case TutorialState.KillEnemy: { if (player.EnemyKilled > 0) { Console.WriteLine("Congratulations, you finished Enemy Kill Phase"); currentState = TutorialState.LevelUp; } else { Console.WriteLine("You need to kill enemy"); } break; } case TutorialState.LevelUp: { if (player.Level > 0) { Console.WriteLine("Congratulations, you finished the whole tutorial"); currentState = TutorialState.LevelUp; } else { Console.WriteLine("You need to level up"); } break; } default: throw new Exception("Not Support"); } } }
static void Main(string[] args) { Player player = new Player(); GameTutorial tutorial = new GameTutorial(player); tutorial.ShowHelpDescription(); tutorial.ValidateState(); //收集黃金 player.CollectGold(1); tutorial.ValidateState(); tutorial.ShowHelpDescription(); //收集木頭 player.CollectIron(1); tutorial.ValidateState(); tutorial.ShowHelpDescription(); //殺敵 player.KillEnemy(); tutorial.ValidateState(); tutorial.ShowHelpDescription(); //升級 player.LevelUp(); tutorial.ValidateState(); }
運行結果
看起來一切都好。。編寫的代碼既能夠根據當前步驟顯示不同的提示,還可以成功的根據玩家的進度切換到下一個步驟。
于是,我自信滿滿的申請了code review,按照我的想法,這段代碼通過code review應該是板上釘釘的事情,誰知,老大看到代碼,差點沒背過氣去。。。稍微平復了一下心情之后,他給了我幾個靈魂拷問。
GameTutorial需要知道各個步驟的滿足條件和提示,它是不是知道的太多了?這符合迪米特法則嗎?
如果我們游戲之后新增一個教程步驟,指導玩家升級武器,是不是GameTutorial需要修改?能有辦法規避這種新增的改動嗎?
如果我們要修改現在的教程步驟之間的順序關系,GameTutorial是不是又不能避免要被動刀?能有辦法盡量減少這種修改的工作量嗎?
Switch case 在現有的情況下已經如此長,如果我們再加入新的步驟,這個方法會變成又臭又長的裹腳布嗎?
本來以為如此簡單的一個功能,沒想到還是有那么多彎彎道道,只怪自己還是太年輕啊!最后他悠悠的告訴我,去看看狀態模式吧,想想這段代碼可以怎么重構。
對象擁有內在狀態,當內在狀態改變時允許其改變行為,這個對象看起來像改變了其類
有點意思,看來我們可以把教程的不同步驟抽象成不同的狀態,然后在各個狀態內部實現切換狀態和顯示幫助文檔的邏輯,這樣做的好處是
符合迪米特法則,把各個步驟所對應的邏輯推遲到子類,教程類就不需要了解每個步驟的邏輯細節,同時隔離了教程類和狀態類,確保狀態類的修改不會影響教程類
符合開閉原則,如果新添加步驟,我們僅僅需要添加步驟子類并修改相鄰的步驟切換邏輯,教程類無需任何改動
接著我們看看UML,
一目了然,在我們的例子里面,state就是教程子步驟,context就是教程類,內部包含教程子步驟并轉發請求給教程子步驟,我們跟著來重構一下代碼吧。
第一步我們需要刪除之前的枚舉,取而代之的是一個抽象類當作狀態基類,即,各個教程步驟類的基類。注意,每個子狀態要自己負責狀態切換,所以我們需要教程類暴露接口以滿足這個功能。
abstract class TutorialState { public abstract void ShowHelpDescription(); public abstract void Validate(GameTutorial tutorial); }
重構教程類體現在以下方面
添加內部狀態表面當前處于哪個步驟,在構造函數中給予初始值
暴露接口以讓子狀態能修改當前狀態以完成狀態切換
因為需要子狀態能訪問玩家當前數據以判斷是否能切換狀態,需要新加接口以避免方法鏈
修改ShowHelpDescription和ValidateState的邏輯,直接轉發方法調用至當前狀態
class GameTutorial { private TutorialState currentState; private Player player; public int PlayerIronNum => player.IronNum; public int PlayerLevel => player.Level; public int PlayerGoldNum => player.GoldNum; public int PlayerEnemyKilled => player.EnemyKilled; public void SetState(TutorialState state) { currentState = state; } public GameTutorial(Player player) { this.player = player; currentState = TutorialStateContext.GetGold; } public void ShowHelpDescription() { currentState.ShowHelpDescription(); } public void ValidateState() { currentState.Validate(this); } }
接著我們創建各個子狀態代表不同的教程步驟
class TutorialSateGetGold : TutorialState { public override void ShowHelpDescription() { Console.WriteLine("Please follow instruction to get gold"); } public override void Validate(GameTutorial tutorial) { if (tutorial.PlayerGoldNum > 0) { Console.WriteLine("Congratulations, you finished Gold Collect Phase"); tutorial.SetState(TutorialStateContext.GetIron); } else { Console.WriteLine("You need to collect gold"); } } } class TutorialStateGetIron : TutorialState { public override void ShowHelpDescription() { Console.WriteLine("Please follow instruction to get Iron"); } public override void Validate(GameTutorial tutorial) { if (tutorial.PlayerIronNum > 0) { Console.WriteLine("Congratulations, you finished Iron Collect Phase"); tutorial.SetState(TutorialStateContext.KillEnemy); } else { Console.WriteLine("You need to collect iron"); } } } class TutorialStateKillEnemy : TutorialState { public override void ShowHelpDescription() { Console.WriteLine("Please follow instruction to kill enemy"); } public override void Validate(GameTutorial tutorial) { if (tutorial.PlayerEnemyKilled > 0) { Console.WriteLine("Congratulations, you finished enemy kill Phase"); tutorial.SetState(TutorialStateContext.LevelUp); } else { Console.WriteLine("You need to collect kill enemy"); } } } class TutorialStateLevelUp : TutorialState { public override void ShowHelpDescription() { Console.WriteLine("Please follow instruction to level up"); } public override void Validate(GameTutorial tutorial) { if (tutorial.PlayerLevel > 0) { Console.WriteLine("Congratulations, you finished the whole tutorial"); } } }
這是模式中沒有提到的知識點,一般來說,為了避免大量的子狀態對象被創建,我們會構造一個狀態容器,以靜態變量的方式初始化需要使用的子狀態。
static class TutorialStateContext { public static TutorialState GetGold; public static TutorialState GetIron; public static TutorialState KillEnemy; public static TutorialState LevelUp; static TutorialStateContext() { GetGold = new TutorialSateGetGold(); GetIron = new TutorialStateGetIron(); KillEnemy = new TutorialStateKillEnemy(); LevelUp = new TutorialStateLevelUp(); } }
測試代碼部分保持不變,直接運行,結果和原來一樣,重構成功。
關于“C#中對象狀態模式怎么實現”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“C#中對象狀態模式怎么實現”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。