C#实现有限状态机

 一、简单描述

状态模式,它的重点有两个,一是对象的状态的切换,二是对象的状态及其导向的行为

对于一个主体对象,当它处于不同的状态,做同一件事情时,它的行为是不一样的。

二、以用户登录为例

一般来说,用户登录状态有两种:未登录、已登录。不管什么状态,使用者既可能会尝试登录,也可能会尝试注销(登出)。那么,程序需要做出以下反应:

当前为未登录状态:

        a、使用者进行登录,程序执行登录

        b、使用者进行注销,程序将驳回

当前为已登录状态:

       c、使用者进行登录,程序将驳回

       d、使用者进行注销,程序执行注销

三、用if判断去实现

一般来说很容易会想到进行各种if...else...判断去完成这项工作

1、初步实现

下面先照着这种想法简单写个用户管理类:

    public class UserManage
    {
        public bool isLoggedIn {//指示登录状态。true表示已登录,false表示未登录
            get
            {
                if (this.Level != null) return true;
                else return false;
            }
        }
        public UserManage.AccountLevel? Level { get; set; }//当前账号等级
        public void LoginOn(string account,string password)//登录
        {
            if (this.isLoggedIn)
            {
                //当前为已登录状态,退出
                return;
            }
            this.Level = verify(account, password);
            if (!this.isLoggedIn)
            {
                //登录失败
            }
            else
            {
                //登录成功
            }
        }
        public void LoginOut()//登出
        {
            if (!this.isLoggedIn)
            { 
                //当前无账号登录,驳回
            }
            else
            {
                //执行账号登出
            }
        }

        public UserManage.AccountLevel verify(string account, string password)//账号验证
        {
            //验证逻辑可根据需要编写
            return UserManage.AccountLevel.Top;
        }
        public enum AccountLevel
        {
            Bottom,
            Top
        }
    }

2、需求增加

初步实现好像也还行,但是上面只是本地账号登录,甲方可能后期变更需求:

支持MES账号登录,由MES系统进行权限下发和管控。

一般来说MES在线模式一直开启,但是为了应对MES系统故障等情况,软件还需要正常使用,所以还需要保留本地账号登录

为了应对这个需求,

加了以下内容

1、isOnline属性用来指示MES在线状态

2、MESVerify方法用于MES账号验证

3、对LoginOn方法进行了变更,加了些判断

整体如下:

    public class UserManage
    {
        public bool isOnline { get; set; }//指示MES在线状态。true表示在线,false表示离线
        public bool isLoggedIn {//指示登录状态。true表示已登录,false表示未登录
            get
            {
                if (this.Level != null) return true;
                else return false;
            }
        }
        public UserManage.AccountLevel? Level { get; set; }//当前账号等级
        public void LoginOn(string account,string password)//登录
        {
            if (this.isOnline)
            {
                if (this.isLoggedIn)
                {
                    //当前为已登录状态,退出
                    return;
                }
                this.Level = MESVerify(account, password);
                if (!this.isLoggedIn)
                {
                    //登录失败
                }
                else
                {
                    //登录成功
                }
            }
            else
            {
                if (this.isLoggedIn)
                {
                    //当前为已登录状态,退出
                    return;
                }
                this.Level = verify(account, password);
                if (!this.isLoggedIn)
                {
                    //登录失败
                }
                else
                {
                    //登录成功
                }
            }
        }
        public void LoginOut()//登出
        {
            if (!this.isLoggedIn)
            { 
                //当前无账号登录,驳回
            }
            else
            {
                //执行账号登出
            }
        }
        public UserManage.AccountLevel MESVerify(string account, string password)//MES账号验证
        {
            //验证逻辑可根据需要编写
            return UserManage.AccountLevel.Top;
        }
        public UserManage.AccountLevel verify(string account, string password)//账号验证
        {
            //验证逻辑可根据需要编写
            return UserManage.AccountLevel.Top;
        }
        public enum AccountLevel
        {
            Bottom,
            Top
        }
    }

3、总结:

有没有感觉每次增加需求,这个类都会变得更臃肿。但这只是简单的用户登录,如果是一些状态稍微复杂的,那么代码的变更和维护将会变得更加困难。

举个例子,一个游戏人物,有“醒”和“睡眠”两种状态,玩家可能会尝试控制人物进行“跑步”的动作。

按照之前的想法,很简单,用if语句判断后执行对应操作即可。但是增加更多的状态,如:受伤、饥饿等等,这样再使用进行判断语句去执行显得很蠢。

四、剖析“状态模式”

现在回到用户登录的例子。不管是什么状态,用户既可能尝试登录也可能登出(注销)。我们将账户管理对象、账户登录状态、执行的动作这三个分离开来。

1、用户管理对象与状态

用户管理对象有两个状态:

2、状态与动作

每种状态均包含“登录”和“登出”两个动作,只不过执行时只用考虑当前状态就可以,而不用再进行繁琐的if判断

 3、状态切换

需要注意执行动作后,对用户管理对象的状态切换

五、用状态模式去实现(简写)

通过拆分把拥有状态的对象、状态、动作三者拆分后,我们可以通过定义接口来规范状态的动作,然后状态类去继承这个接口,并实现其成员(实现动作)即可。

当需要增加新状态时,只需要像已有的状态一样,继承接口并实现其成员即可。

下面来写一下本地账户登录的简单实现,MES登录不在赘述:

1、定义接口

首先定义一个接口,它规定状态对象需要实现的方法(规定需要做的事)。

    /*
     * 用于人员登录的状态模式
     * 1、尝试登录
     * 2、尝试注销
     */
    /// <summary>定义人员登录动作
    /// 
    /// </summary>
    public interface IState
    {
        /// <summary>登录
        /// 
        /// </summary>
        void Logon(string userName, string password);
        /// <summary>注销
        /// 
        /// </summary>
        void Logout();
    }

2、定义状态类

定义已登录状态的类和未登录状态的类,并继承上面的接口

    /// <summary>未登录状态</summary>
    public class NotLoggedIn:IState
    {
        private UserSate _useState;
        public NotLoggedIn(UserSate userState)
        {
            this._useState = userState;//对主体对象进行引用
        }
        /// <summary>尝试登录</summary>
        public void Logon(string userName, string password)
        {
            //进行登录,然后切换为已登录状态。
            //如果成功,那就切换主体对象的状态
            this._useState.State = this._useState.LoggedIn;
        }
        /// <summary>尝试注销</summary>
        public void Logout()
        {
            //当前为未登录状态,无法切换状态。不切换状态
        }
    }


    /// <summary>已登录状态</summary>
    public class LoggedIn : IState
    {
        private UserSate _useState;
        public LoggedIn(UserSate userState)
        {
            this._useState = userState;//对主体对象进行引用
        }
        /// <summary>尝试登录</summary>
        public void Logon(string userName, string password)
        {
            //驳回。不切换状态
        }
        /// <summary>尝试注销</summary>
        public void Logout()
        {
            //注销。切换为未登录状态
            this._useState.State = this._useState.NotLoggedIn;//切换状态
        }
    }

 3、定义用户管理类

    /// <summary>用户管理类</summary>
    public class UserSate
    {
        /// <summary>非登录状态</summary>
        internal IState NotLoggedIn { get; set; }
        /// <summary>已登录状态</summary>
        internal IState LoggedIn { get; set; }
        /// <summary>当前用户状态</summary>
        public IState State { get; set; }


        /// <summary>实例化一个用户状态对象</summary>
        public UserSate()
        {
            this.NotLoggedIn = new NotLoggedIn(this);
            this.LoggedIn = new LoggedIn(this);
            this.State = this.NotLoggedIn;//默认为非登录状态
        }

        /// <summary>尝试登录</summary>
        public void Logon(string userName, string password)
        {
            this.State.Logon(userName, password);
        }
        /// <summary>尝试注销</summary>
        public void Logout()
        {
            this.State.Logout();
        }
    }

六、完整实现

1、接口

namespace StatePattern.CustomerStateInterface
{
    /*
     * 用于人员登录的状态模式
     * 1、尝试登录
     * 2、尝试注销
     */
    /// <summary>定义人员登录动作
    /// 
    /// </summary>
    public interface IState
    {
        /// <summary>登录
        /// 
        /// </summary>
        StatePattern.UserInfo.UserSate.OperationInfo Logon(string userName, string password);
        /// <summary>注销
        /// 
        /// </summary>
        StatePattern.UserInfo.UserSate.OperationInfo Logout();
    
    }
}

2、状态类

using StatePattern.CustomerStateInterface;
using StatePattern.UserInfo;

namespace StatePattern.State
{ 
    /*
     * 用户登录状态
     * 1、用户只会尝试2种动作:
     *      a、尝试登录
     *      b、尝试注销
     * 2、登录状态只会有2种状态
     *      a、未登录
     *      b、已登录
     */

    /// <summary>未登录状态</summary>
    public class NotLoggedIn:IState
    {
        private UserSate _useState;
        public NotLoggedIn(UserSate userState)
        {
            this._useState = userState;//对主体对象进行引用
        }
        /// <summary>尝试登录</summary>
        public StatePattern.UserInfo.UserSate.OperationInfo Logon(string userName, string password)
        { 
        //进行登录,然后切换为已登录状态
            this._useState.UserLevel = ToolClass.Verify(userName, password);
            if (this._useState.UserLevel == null)
            {
                this._useState.UserName = "无";
                return new UserSate.OperationInfo() { successful = false, Message = "登录未通过" };
            }
            else
            {
                this._useState.State = this._useState.LoggedIn;//切换状态
                this._useState.UserName = userName;
                return new UserSate.OperationInfo() { successful = true, Message = "登录验证通过" };
            } 
        }
        /// <summary>尝试注销</summary>
        public StatePattern.UserInfo.UserSate.OperationInfo Logout()
        { 
        //当前为未登录状态,不切换状态
            this._useState.UserName = "无";
            this._useState.UserLevel = null ;
            return new UserSate.OperationInfo() { successful = false, Message = "当前用户未登录" };
        }
    }


    /// <summary>已登录状态</summary>
    public class LoggedIn : IState
    {
        private UserSate _useState;
        public LoggedIn(UserSate userState)
        {
            this._useState = userState;//对主体对象进行引用
        }
        /// <summary>尝试登录</summary>
        public StatePattern.UserInfo.UserSate.OperationInfo Logon(string userName, string password)
        {
            //驳回。不切换状态
            return new UserSate.OperationInfo() { successful = false, Message = "当前已经登录" };
        }
        /// <summary>尝试注销</summary>
        public StatePattern.UserInfo.UserSate.OperationInfo Logout()
        {
            //注销。切换为未登录状态
            this._useState.State = this._useState.NotLoggedIn;//切换状态
            this._useState.UserName = "无";
            this._useState.UserLevel = null;
            return new UserSate.OperationInfo() { successful = true, Message = "注销成功" };
        }
    }
}

 3、用户管理类

using StatePattern.CustomerStateInterface;
using StatePattern.State;

namespace StatePattern.UserInfo
{
    /// <summary>用户状态</summary>
    public class UserSate
    {
        /// <summary>非登录状态</summary>
        internal IState NotLoggedIn { get; set; }
        /// <summary>已登录状态</summary>
        internal IState LoggedIn { get; set; }
        /// <summary>当前用户状态</summary>
        public IState State { get; set; }
        /// <summary>是否已登录</summary>
        public bool IsLoggedIn
        {
            get {
                if (this.State == this.LoggedIn) return true;
                else return false;
            }
        }

        /// <summary>用户名</summary>
        public string UserName { get; set; }
        /// <summary>用户等级</summary>
        public int? UserLevel { get; set; }

        /// <summary>实例化一个用户状态对象</summary>
        public UserSate()
        {
            this.NotLoggedIn = new NotLoggedIn(this);
            this.LoggedIn = new LoggedIn(this);
            this.State = this.NotLoggedIn;//默认为非登录状态
            this.UserName = "无";
            this.UserLevel = null ;
        }

        /// <summary>尝试登录</summary>
        public OperationInfo Logon(string userName, string password)
        {
            return this.State.Logon(userName, password);
        }
        /// <summary>尝试注销</summary>
        public OperationInfo Logout()
        {
           return this.State.Logout();
        }

        /// <summary>操作消息</summary>
        public class OperationInfo
        {
            /// <summary>操作是否成功</summary>
            public bool successful { get; set; }
            /// <summary>消息</summary>
            public string Message { get; set; }
        }
    }
}

4、账号验证方法

随便写的,可以自由定义规则

namespace StatePattern
{
    public static class ToolClass
    {
        /// <summary>验证账户是否通过
        /// 
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <param name="password">密码</param>
        /// <returns></returns>
        public static int? Verify(string userName, string password)
        {
            //假设只有当用户名为ADMIN、密码为123时,登录验证通过
            //level等级为null时表示为未通过验证,1到10表示等级高低,可以定义
            if (userName == "ADMIN" && password == "123") return 10;
            else return null;
        }
    }
}

5、简单的调用展示

 随便建了个winform程序,就不赘述了,看图。

 

using StatePattern.UserInfo;

namespace WindowsFormsApplication4_状态机的简单实现_
{
    public partial class Form1 : Form
    {
        public UserSate user;
        public Form1()
        {
            InitializeComponent();
            this.user = new UserSate();
            this.label_DisplayStatus.Text = string.Format("当前登录用户:{0}  等级:{1}", this.user.UserName, this.user.UserLevel.ToString());
        }

        private void button_Logon_Click(object sender, EventArgs e)//点击了登录
        {
            UserSate.OperationInfo info = this.user.Logon(this.textBox_userName.Text, this.textBox_password.Text);
            this.label_DisplayStatus.Text = string.Format("当前登录用户:{0}  等级:{1}", this.user.UserName, this.user.UserLevel.ToString());
            MessageBox.Show(string.Format("操作是否成功:{0}\r\n操作消息:{1}", info.successful, info.Message));
        }

        private void button_Logout_Click(object sender, EventArgs e)//点击了注销
        {
            UserSate.OperationInfo info = this.user.Logout();
            this.label_DisplayStatus.Text = string.Format("当前登录用户:{0}  等级:{1}", this.user.UserName, this.user.UserLevel.ToString());
            MessageBox.Show(string.Format("操作是否成功:{0}\r\n操作消息:{1}", info.successful, info.Message));
        }
    }
}

6、效果

 未登录状态下,尝试用用户名:ADMIN,密码:123登录

 

 已登录状态下,任意账号登录

 

已注销的情况下再次点击注销

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要创建一个有限状态机(Finite State Machine)的游戏框架,可以按照以下步骤进行: 1. 定义状态(States):首先,确定游戏中的各个状态。例如,你的游戏可能有开始状态、进行中状态、暂停状态和结束状态等。 2. 定义状态转换(State Transitions:确定状态之间的转换条件。例如,当游戏从开始状态转换到进行中状态时,可能需要玩家点击开始按钮。 3. 实现状态类:为每个状态创建一个类,该类负责处理该状态下的逻辑和行为。每个状态类应该有进入状态、更新状态和退出状态的方法。 4. 创建状态管理器(State Manager):实现一个状态管理器来管理各个状态之间的转换。状态管理器应该能够根据当前状态和转换条件,切换到下一个合适的状态。 5. 在游戏主循环中使用状态管理器:在游戏的主循环中,使用状态管理器来更新当前状态,并执行该状态下的逻辑和行为。 下面是一个简单的示例代码,展示了如何使用 C# 创建一个简单的有限状态机游戏框架: ```csharp // 定义游戏状态枚举 enum GameState { Start, Playing, Paused, End } // 状态基类 abstract class State { public abstract void Enter(); public abstract void Update(); public abstract void Exit(); } // 开始状态 class StartState : State { public override void Enter() { Console.WriteLine("Game started"); } public override void Update() { // 处理状态转换条件 if (Keyboard.IsKeyDown(Keys.Space)) { GameStateManager.ChangeState(GameState.Playing); } } public override void Exit() { Console.WriteLine("Leaving start state"); } } // 进行中状态 class PlayingState : State { public override void Enter() { Console.WriteLine("Game playing"); } public override void Update() { // 处理状态转换条件 if (Keyboard.IsKeyDown(Keys.P)) { GameStateManager.ChangeState(GameState.Paused); } else if (GameOverCondition) { GameStateManager.ChangeState(GameState.End); } } public override void Exit() { Console.WriteLine("Leaving playing state"); } } // 暂停状态 class PausedState : State { public override void Enter() { Console.WriteLine("Game paused"); } public override void Update() { // 处理状态转换条件 if (Keyboard.IsKeyDown(Keys.R)) { GameStateManager.ChangeState(GameState.Playing); } } public override void Exit() { Console.WriteLine("Leaving paused state"); } } // 结束状态 class EndState : State { public override void Enter() { Console.WriteLine("Game ended"); } public override void Update() { // 处理状态转换条件 if (Keyboard.IsKeyDown(Keys.Space)) { GameStateManager.ChangeState(GameState.Start); } } public override void Exit() { Console.WriteLine("Leaving end state"); } } // 状态管理器 class GameStateManager { private static State currentState; public static void ChangeState(GameState newState) { // 退出当前状态 currentState?.Exit(); // 创建新状态实例 switch (newState) { case GameState.Start: currentState = new StartState(); break; case GameState.Playing: currentState = new PlayingState(); break; case GameState.Paused: currentState = new PausedState(); break; case GameState.End: currentState = new EndState(); break; } // 进入新状态 currentState?.Enter(); } public static void UpdateCurrentState() { currentState?.Update(); } } // 游戏主循环 class GameLoop { static void Main(string[] args) { // 初始化游戏状态 GameStateManager.ChangeState(GameState.Start); // 游戏主循环 while (true) { // 更新当前状态 GameStateManager.UpdateCurrentState(); // 其他游戏逻辑 Thread.Sleep(16); // 控制帧率 } } } ``` 这只是一个简单的示例,你可以根据你的具体需求进行扩展和修改。希望这可以帮助到你!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值