溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C#中對象狀態(tài)模式怎么實(shí)現(xiàn)

發(fā)布時間:2022-06-02 09:22:34 來源:億速云 閱讀:113 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹了C#中對象狀態(tài)模式怎么實(shí)現(xiàn)的相關(guān)知識,內(nèi)容詳細(xì)易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇C#中對象狀態(tài)模式怎么實(shí)現(xiàn)文章都會有所收獲,下面我們一起來看看吧。

    定義枚舉表示教程進(jìn)度

    首先用一個枚舉,表示教程進(jìn)行的不同程度

    enum TutorialState
    {
        GetGold,
        GetIron,
        KillEnemy,
        LevelUp
    }

    定義角色類

    無需多言,封裝收集到的資源數(shù)、擊殺敵人數(shù)量、角色等級和一些升級接口等

    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++;
        }
    }

    定義教程類

    定義一個教程類,包括

    • 顯示幫助文字以協(xié)助玩家通過當(dāng)前教程步驟

    • 判斷玩家是否已經(jīng)完成當(dāng)前教程步驟,若是,切換到下一個步驟直到完成教程

    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();
    }

    運(yùn)行結(jié)果

    C#中對象狀態(tài)模式怎么實(shí)現(xiàn)

    看起來一切都好。。編寫的代碼既能夠根據(jù)當(dāng)前步驟顯示不同的提示,還可以成功的根據(jù)玩家的進(jìn)度切換到下一個步驟。

    于是,我自信滿滿的申請了code review,按照我的想法,這段代碼通過code review應(yīng)該是板上釘釘?shù)氖虑?,誰知,老大看到代碼,差點(diǎn)沒背過氣去。。。稍微平復(fù)了一下心情之后,他給了我?guī)讉€靈魂拷問。

    • GameTutorial需要知道各個步驟的滿足條件和提示,它是不是知道的太多了?這符合迪米特法則嗎?

    • 如果我們游戲之后新增一個教程步驟,指導(dǎo)玩家升級武器,是不是GameTutorial需要修改?能有辦法規(guī)避這種新增的改動嗎?

    • 如果我們要修改現(xiàn)在的教程步驟之間的順序關(guān)系,GameTutorial是不是又不能避免要被動刀?能有辦法盡量減少這種修改的工作量嗎?

    • Switch case 在現(xiàn)有的情況下已經(jīng)如此長,如果我們再加入新的步驟,這個方法會變成又臭又長的裹腳布嗎?

    本來以為如此簡單的一個功能,沒想到還是有那么多彎彎道道,只怪自己還是太年輕??!最后他悠悠的告訴我,去看看狀態(tài)模式吧,想想這段代碼可以怎么重構(gòu)。

    狀態(tài)模式出場

    定義

    對象擁有內(nèi)在狀態(tài),當(dāng)內(nèi)在狀態(tài)改變時允許其改變行為,這個對象看起來像改變了其類

    有點(diǎn)意思,看來我們可以把教程的不同步驟抽象成不同的狀態(tài),然后在各個狀態(tài)內(nèi)部實(shí)現(xiàn)切換狀態(tài)和顯示幫助文檔的邏輯,這樣做的好處是

    • 符合迪米特法則,把各個步驟所對應(yīng)的邏輯推遲到子類,教程類就不需要了解每個步驟的邏輯細(xì)節(jié),同時隔離了教程類和狀態(tài)類,確保狀態(tài)類的修改不會影響教程類

    • 符合開閉原則,如果新添加步驟,我們僅僅需要添加步驟子類并修改相鄰的步驟切換邏輯,教程類無需任何改動

    接著我們看看UML,

    C#中對象狀態(tài)模式怎么實(shí)現(xiàn)

    一目了然,在我們的例子里面,state就是教程子步驟,context就是教程類,內(nèi)部包含教程子步驟并轉(zhuǎn)發(fā)請求給教程子步驟,我們跟著來重構(gòu)一下代碼吧。

    代碼重構(gòu)

    創(chuàng)建狀態(tài)基類

    第一步我們需要刪除之前的枚舉,取而代之的是一個抽象類當(dāng)作狀態(tài)基類,即,各個教程步驟類的基類。注意,每個子狀態(tài)要自己負(fù)責(zé)狀態(tài)切換,所以我們需要教程類暴露接口以滿足這個功能。

    abstract class TutorialState
    {
        public abstract void ShowHelpDescription();
        public abstract void Validate(GameTutorial tutorial);
    }

    重構(gòu)教程類

    重構(gòu)教程類體現(xiàn)在以下方面

    • 添加內(nèi)部狀態(tài)表面當(dāng)前處于哪個步驟,在構(gòu)造函數(shù)中給予初始值

    • 暴露接口以讓子狀態(tài)能修改當(dāng)前狀態(tài)以完成狀態(tài)切換

    • 因?yàn)樾枰訝顟B(tài)能訪問玩家當(dāng)前數(shù)據(jù)以判斷是否能切換狀態(tài),需要新加接口以避免方法鏈

    • 修改ShowHelpDescription和ValidateState的邏輯,直接轉(zhuǎn)發(fā)方法調(diào)用至當(dāng)前狀態(tài)

    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);
        }
    }

    創(chuàng)建各個子狀態(tài)

    接著我們創(chuàng)建各個子狀態(tài)代表不同的教程步驟

    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");
            }
        }
    }

    添加狀態(tài)容器

    這是模式中沒有提到的知識點(diǎn),一般來說,為了避免大量的子狀態(tài)對象被創(chuàng)建,我們會構(gòu)造一個狀態(tài)容器,以靜態(tài)變量的方式初始化需要使用的子狀態(tài)。

    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();
        }
    }

    測試代碼部分保持不變,直接運(yùn)行,結(jié)果和原來一樣,重構(gòu)成功。

    關(guān)于“C#中對象狀態(tài)模式怎么實(shí)現(xiàn)”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“C#中對象狀態(tài)模式怎么實(shí)現(xiàn)”知識都有一定的了解,大家如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI