iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C#中对象状态模式教程示例
  • 112
分享到

C#中对象状态模式教程示例

2024-04-02 19:04:59 112人浏览 独家记忆
摘要

目录真实的故事定义枚举表示教程进度定义角色类定义教程类测试代码状态模式出场定义代码重构创建状态基类重构教程类创建各个子状态添加状态容器结语真实的故事 当老胡还是小胡的时候,跟随团队一

真实的故事

当老胡还是小胡的时候,跟随团队一起开发一款游戏。这款游戏是一款末日生存类游戏,玩家可以

  • 收集资源,两种,一种金子,一种铁。
  • 升级自身
  • 击杀敌人
  • 用资源合成装备

项目开发的很顺利,我那时得到一个任务,是为游戏做一个新手教程,在这个教程里面,通过一系列步骤,引导新手玩家熟悉这个游戏。游戏设计给出的教程包含以下步骤

  • 收集金子
  • 收集铁
  • 击杀敌人
  • 升级

同时要求在不用的阶段显示不同的提示以正确引导玩家。考虑合成装备算是高级玩家才会接触到的功能,所以暂时不打算放在新手教程里面。

当老大把任务交给我的时候,我感觉简单爆了,不就写一个新手教程么,要求又那么明确,应该要不了多少时间。于是,一个上午过后,我交出了如下代码。 

定义枚举表示教程进度

首先用一个枚举,表示教程进行的不同程度

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

测试代码部分保持不变,直接运行,结果和原来一样,重构成功。

结语

  • 这就是状态模式和它的使用场景,比较一下重构前和重构后的代码,发现代码通过重构满足了开闭原则和迪米特法则,相信重构后的代码能通过code review吧。_
  • 不过状态模式虽然好,也有自己的缺点,因为需要一个子类对应一个子状态,那么子状态太多的时候,就会出现类爆炸的情况。还请大家多注意。
  • 作为行为模式之一的状态模式,在日常开发中出现的频率还是挺高的,比如游戏中经常用到的状态机,就是状态模式的一种应用场景,大家在平时工作中保持善于观察的眼睛,就能学到更多的东西。

以上就是C#中对象状态模式 教程示例的详细内容,更多关于C#对象状态模式 的资料请关注编程网其它相关文章!

--结束END--

本文标题: C#中对象状态模式教程示例

本文链接: https://www.lsjlt.com/news/150385.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C#中对象状态模式教程示例
    目录真实的故事定义枚举表示教程进度定义角色类定义教程类测试代码状态模式出场定义代码重构创建状态基类重构教程类创建各个子状态添加状态容器结语真实的故事 当老胡还是小胡的时候,跟随团队一...
    99+
    2024-04-02
  • C#中对象状态模式怎么实现
    这篇文章主要介绍了C#中对象状态模式怎么实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C#中对象状态模式怎么实现文章都会有所收获,下面我们一起来看看吧。定义枚举表示教程进度首先用一个枚举,表示教程进行的不同...
    99+
    2023-06-30
  • Java状态设计模式实现对象状态转换的优雅方式
    目录介绍实现总结优点缺点应用场景介绍 Java状态模式(State Pattern)是一种面向对象的设计模式,它将对象的状态封装成独立的状态对象,并将对象的行为与状态对象解耦,它允许...
    99+
    2023-05-17
    Java状态设计模式 状态模式实现对象状态转换
  • java设计模式之状态模式的示例分析
    这篇文章给大家分享的是有关java设计模式之状态模式的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。 定义 很多时候,一个对象的行为会根据一个动态的属性变化而变化,这...
    99+
    2024-04-02
  • vue中状态管理模式vuex的示例分析
    这篇文章主要介绍了vue中状态管理模式vuex的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。vuex是一个专门为vue.js设计的...
    99+
    2024-04-02
  • vuejs中vuex状态管理模式的示例分析
    这篇文章给大家分享的是有关vuejs中vuex状态管理模式的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。关于vuex类的新闻最近很多,看到眼热就去查了下资料,然后扯出来...
    99+
    2024-04-02
  • vuex状态管理模式的示例分析
    小编给大家分享一下vuex状态管理模式的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、前言本次接受一个BI系统,要求...
    99+
    2024-04-02
  • JS设计模式之状态模的示例分析
    这篇文章主要介绍了JS设计模式之状态模的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。1. 概述当一个对象的内在状态改变时允许改变其...
    99+
    2024-04-02
  • Java设计模式之备忘录模式实现对象状态的保存和恢复
    目录介绍实现备忘录类发起人类管理者类测试总结优点缺点应用场景介绍 备忘录模式(Memento Pattern)是一种行为型设计模式,它允许在不破坏封装性的前提下,捕获并保存一个对象的...
    99+
    2023-05-17
    Java设计模式 Java备忘录模式
  • C++中类与对象的示例分析
    这篇文章将为大家详细讲解有关C++中类与对象的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。前言:C 语言是面向过程的,关注的是过程,分析出求解的步骤,通过函数逐步调用解决问题。C++是基于面向对...
    99+
    2023-06-22
  • C++中对象排序的示例分析
    这篇文章主要介绍C++中对象排序的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1.对象比较介绍在排序中进行交换的前提主要是进行对象间的 比较、而常见的排序是对一个数组排序,然后对每个数组内容进行比...
    99+
    2023-06-29
  • JS设计模式之数据访问对象模式的示例分析
    这篇文章主要为大家展示了“JS设计模式之数据访问对象模式的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“JS设计模式之数据访问对象模式的示例分析”这篇文...
    99+
    2024-04-02
  • 打印机状态已中止修复实例教程
    在我们在应用打印机打印文档的情况下,有时会发觉打印机状态已中止,打印不起来了,此刻如何恢复呢,实际上只必须取消掉打印中止就可以了。打印机状态已中止如何恢复:最先根据右下方小图标开启打印机设置。开启后,能够看见表明“已中止”此刻,大家点一下右...
    99+
    2023-07-10
  • C#中的composite模式示例详解
    目录写在前面一个简单例子基层员工类经理类公司架构类客户端代码再想一下使用组合模式进行重构透明型安全型重构后的代码(透明型)写在前面 Composite组合模式属于设计模式中比较热门的...
    99+
    2024-04-02
  • C语言中有哪些进程的状态模型
    本篇文章给大家分享的是有关C语言中有哪些进程的状态模型,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。什么是状态机定义状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一...
    99+
    2023-06-16
  • C++函数模板学习示例教程指南
    目录C++函数模板学习指南1. 函数模板的定义2. 函数模板的使用3. 函数模板的特化4. 函数模板的偏特化6. 非类型模板参数7. 函数模板的局限性总结C++函数模板学习指南 C...
    99+
    2023-05-16
    C++函数模板学习 C++函数模板教程
  • Xamarin.Forms中程序状态与进度的示例分析
    小编给大家分享一下Xamarin.Forms中程序状态与进度的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Xamarin.Forms中程序状态与进度在X...
    99+
    2023-06-04
  • C#面向对象编程中开闭原则的示例详解
    目录开闭原则C# 示例改进总结在面向对象编程中,SOLID 是五个设计原则的首字母缩写,旨在使软件设计更易于理解、灵活和可维护。这些原则是由美国软件工程师和讲师罗伯特&mi...
    99+
    2024-04-02
  • java 中设计模式(值对象)的实例详解
    java 中设计模式(值对象)的实例详解应用场景:在Java开发时,需要来回交换大量的数据,比如要为方法传入参数,也要获取方法的返回值,该如何能更好的进行数据的交互?这个时候就需要用到我们的值对象设计模式值对象的本质是“封装数据具体步骤:&...
    99+
    2023-05-31
    java 值对象 ava
  • C++云编程:面向对象方法与设计模式
    面向对象方法和设计模式在 c++++ 云编程中的作用面向对象编程 (oop) 通过封装、继承和多态增强了 c++ 云应用程序的可伸缩性、可维护性和可重用性。设计模式提供经过验证的解决方案...
    99+
    2024-05-12
    c++ 云编程
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作