最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C#中對象狀態(tài)模式教程示例

 更新時間:2022年06月01日 16:04:06   作者:老胡寫代碼  
這篇文章主要為大家介紹了C#中對象狀態(tài)模式的教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

真實的故事

當老胡還是小胡的時候,跟隨團隊一起開發(fā)一款游戲。這款游戲是一款末日生存類游戲,玩家可以

  • 收集資源,兩種,一種金子,一種鐵。
  • 升級自身
  • 擊殺敵人
  • 用資源合成裝備

項目開發(fā)的很順利,我那時得到一個任務,是為游戲做一個新手教程,在這個教程里面,通過一系列步驟,引導新手玩家熟悉這個游戲。游戲設計給出的教程包含以下步驟

  • 收集金子
  • 收集鐵
  • 擊殺敵人
  • 升級

同時要求在不用的階段顯示不同的提示以正確引導玩家??紤]合成裝備算是高級玩家才會接觸到的功能,所以暫時不打算放在新手教程里面。

當老大把任務交給我的時候,我感覺簡單爆了,不就寫一個新手教程么,要求又那么明確,應該要不了多少時間。于是,一個上午過后,我交出了如下代碼。 

定義枚舉表示教程進度

首先用一個枚舉,表示教程進行的不同程度

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

定義教程類

定義一個教程類,包括

  • 顯示幫助文字以協(xié)助玩家通過當前教程步驟
  • 判斷玩家是否已經完成當前教程步驟,若是,切換到下一個步驟直到完成教程
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應該是板上釘釘的事情,誰知,老大看到代碼,差點沒背過氣去。。。稍微平復了一下心情之后,他給了我?guī)讉€靈魂拷問。

  • GameTutorial需要知道各個步驟的滿足條件和提示,它是不是知道的太多了?這符合迪米特法則嗎?
  • 如果我們游戲之后新增一個教程步驟,指導玩家升級武器,是不是GameTutorial需要修改?能有辦法規(guī)避這種新增的改動嗎?
  • 如果我們要修改現在的教程步驟之間的順序關系,GameTutorial是不是又不能避免要被動刀?能有辦法盡量減少這種修改的工作量嗎?
  • Switch case 在現有的情況下已經如此長,如果我們再加入新的步驟,這個方法會變成又臭又長的裹腳布嗎?

本來以為如此簡單的一個功能,沒想到還是有那么多彎彎道道,只怪自己還是太年輕?。∽詈笏朴频母嬖V我,去看看狀態(tài)模式吧,想想這段代碼可以怎么重構。

狀態(tài)模式出場

定義

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

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

  • 符合迪米特法則,把各個步驟所對應的邏輯推遲到子類,教程類就不需要了解每個步驟的邏輯細節(jié),同時隔離了教程類和狀態(tài)類,確保狀態(tài)類的修改不會影響教程類
  • 符合開閉原則,如果新添加步驟,我們僅僅需要添加步驟子類并修改相鄰的步驟切換邏輯,教程類無需任何改動

接著我們看看UML,

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

代碼重構

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

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

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

重構教程類

重構教程類體現在以下方面

  • 添加內部狀態(tài)表面當前處于哪個步驟,在構造函數中給予初始值
  • 暴露接口以讓子狀態(tài)能修改當前狀態(tài)以完成狀態(tài)切換
  • 因為需要子狀態(tài)能訪問玩家當前數據以判斷是否能切換狀態(tài),需要新加接口以避免方法鏈
  • 修改ShowHelpDescription和ValidateState的邏輯,直接轉發(fā)方法調用至當前狀態(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)容器

這是模式中沒有提到的知識點,一般來說,為了避免大量的子狀態(tài)對象被創(chuàng)建,我們會構造一個狀態(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();
    }
}

測試代碼部分保持不變,直接運行,結果和原來一樣,重構成功。

結語

  • 這就是狀態(tài)模式和它的使用場景,比較一下重構前和重構后的代碼,發(fā)現代碼通過重構滿足了開閉原則和迪米特法則,相信重構后的代碼能通過code review吧。_
  • 不過狀態(tài)模式雖然好,也有自己的缺點,因為需要一個子類對應一個子狀態(tài),那么子狀態(tài)太多的時候,就會出現類爆炸的情況。還請大家多注意。
  • 作為行為模式之一的狀態(tài)模式,在日常開發(fā)中出現的頻率還是挺高的,比如游戲中經常用到的狀態(tài)機,就是狀態(tài)模式的一種應用場景,大家在平時工作中保持善于觀察的眼睛,就能學到更多的東西。

以上就是C#中對象狀態(tài)模式 教程示例的詳細內容,更多關于C#對象狀態(tài)模式 的資料請關注腳本之家其它相關文章!

相關文章

  • 使用遞歸算法求第30位數的值

    使用遞歸算法求第30位數的值

    這篇文章主要介紹了使用遞歸求第30位數的值,需要的朋友可以參考下
    2014-02-02
  • C#中Array的存儲結構簡單介紹

    C#中Array的存儲結構簡單介紹

    本文將從一個數組的基礎操作開始,逐步來推導數組的在C#基礎操作、數組在CoreCLR的維護策略,數組在C++的內存分配等階段具體是如何實現的,感興趣的朋友跟隨小編一起看看吧
    2023-11-11
  • DevExpress實現TreeList按條件隱藏節(jié)點CheckBox的方法

    DevExpress實現TreeList按條件隱藏節(jié)點CheckBox的方法

    這篇文章主要介紹了DevExpress實現TreeList按條件隱藏節(jié)點CheckBox的方法,需要的朋友可以參考下
    2014-08-08
  • C#實現TFTP客戶端的項目實踐

    C#實現TFTP客戶端的項目實踐

    TFTP不僅有斷點續(xù)傳,多用戶級別限制等功能,本文主要介紹了C#實現TFTP客戶端的項目實踐,具有一定的參考價值,感興趣的可以了解一下
    2024-04-04
  • C#的winform控件命名規(guī)范

    C#的winform控件命名規(guī)范

    這篇文章主要介紹了C#的winform控件命名規(guī)范,對各種常用控件的命名規(guī)范做了較為詳細的講解,需要的朋友可以參考下
    2015-01-01
  • c# 用ELMAH日志組件處理異常

    c# 用ELMAH日志組件處理異常

    這篇文章主要介紹了c# 用ELMAH日志組件處理異常,幫助大家更好的理解和使用c#,感興趣的朋友可以了解下
    2021-01-01
  • c#如何利用定時器自動備份數據庫詳解

    c#如何利用定時器自動備份數據庫詳解

    在開發(fā)過程當中,你一定遇到文件損壞活或丟失的煩惱,而每天備份又很麻煩,你只要設置每天備份的時間,并將程序加入啟動項中,就可以自動完成備份,這篇文章主要給大家介紹了關于c#如何利用定時器自動備份數據庫的相關資料,需要的朋友可以參考下
    2021-10-10
  • 一起來學習C#的觀察者模式

    一起來學習C#的觀察者模式

    這篇文章主要為大家詳細介紹了C#的觀察者模式,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • 使用C#判斷一個字符串是否包含大寫字符的五種方法

    使用C#判斷一個字符串是否包含大寫字符的五種方法

    本文提供了五種判斷字符串是否包含大寫字符的方法及其實現源碼,文中通過代碼示例給大家介紹的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下
    2023-12-12
  • unity 如何修改材質屬性和更換shader

    unity 如何修改材質屬性和更換shader

    這篇文章主要介紹了unity 修改材質屬性和更換shader的操作方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04

最新評論

招远市| 田阳县| 连州市| 剑川县| 托克托县| 陈巴尔虎旗| 崇明县| 湖南省| 黄浦区| 额尔古纳市| 酒泉市| 陆河县| 荆州市| 乌拉特中旗| 晋江市| 安平县| 黔西| 安多县| 会同县| 屏东市| 南平市| 咸阳市| 长葛市| 荣昌县| 山西省| 闽侯县| 龙陵县| 元氏县| 本溪市| 浦东新区| 大方县| 景宁| 连江县| 康平县| 泗水县| 武夷山市| 武夷山市| 湖南省| 洮南市| 城固县| 达孜县|