(20)行为型模式——状态

行为型模式——状态(State)

问题背景

当希望对象的行为会随着状态改变而改变时,考虑使用状态。想想LoL,玩家可以让他们操控的英雄移动、攻击、使用技能等,而英雄在一局游戏里会有很多种状态,比如正常、眩晕、沉默、死亡…英雄在不同状态下对不同指令的响应方式可能会有不同。正常状态下英雄会响应任何指令,而眩晕状态下则只响应特定的技能(净化、水银、五秒真男人…),沉默状态下不会使用技能,死亡状态下只能看装备…

按照套路,这种情况肯定是不能直接用if或者switch的,因为状态的种类可能会变多(@睡眠@缴械),为了应对变化,就需要把状态这个概念抽象出来。

解决方案

本来,我们可以在英雄类里处理各种指令的响应逻辑。但上面也说了,当状态种类变多时,就得修改英雄类,破坏了开闭原则。于是,我们把这部分逻辑从英雄类中剥离出来,交给一个“状态”类来实现。提取出一个状态接口IState,包含Move,Attack和UseSkill方法,英雄类持有一个对IState的引用,当英雄收到指令时,就把指令转发给IState做处理。使用状态后,程序结构是这样的:
程序结构

效果

  1. 将不同状态的逻辑从对象中剥离,增强了系统的可扩展性。
  2. 使对象能显示地转换状态,增强了系统的可理解性。

缺陷

很多时候,状态对象本身会改变上下文的状态,因此状态对象需要持有对上下文的引用。为了在状态对象中直接转换状态,各个具体对象之间必须了解彼此,无法做到完全解耦,当加入新状态时,总会有地方需要修改。

相关模式

  1. 享元:状态对象的一些属性可以实现共享。
  2. 单例:无状态的状态对象可以实现为单例。

实现

using System;

namespace State
{
    class Client
    {
        public interface IState
        {
            void Move();
            void Attack();
            void UseSkill();
            void ChangeState();
        }

        public class Champion
        {
            public int Count { get; set; }
            public IState State { get; set; }
            public void Move()
            {
                State.Move();
            }
            public void Attack()
            {
                State.Attack();
            }
            public void UseSkill()
            {
                State.UseSkill();
            }
            public void ChangeState()
            {
                if (State == null)
                {
                    State = new Normal(this);
                } else
                {
                    State.ChangeState();
                }
            }
        }

        public class Normal : IState
        {
            private Champion champion;
            public Normal(Champion champion)
            {
                this.champion = champion;
            }
            public void Move()
            {
                Console.WriteLine("状态正常,移动");
            }
            public void Attack()
            {
                Console.WriteLine("状态正常,攻击");
            }
            public void UseSkill()
            {
                Console.WriteLine("状态正常,使用技能");
            }
            public void ChangeState()
            {
                switch (champion.Count++ % 3)
                {
                    case 0:
                        Console.WriteLine("你被眩晕");
                        champion.State = new Stun(champion);
                        break;
                    case 1:
                        Console.WriteLine("你被沉默");
                        champion.State = new Silence(champion);
                        break;
                    case 2:
                        Console.WriteLine("你死了");
                        champion.State = new Dead(champion);
                        break;
                }
            }
        }

        public class Stun : IState
        {
            private Champion champion;
            public Stun(Champion champion)
            {
                this.champion = champion;
            }
            public void Move()
            {
                Console.WriteLine("眩晕状态,不能移动");
            }
            public void Attack()
            {
                Console.WriteLine("眩晕状态,不能攻击");
            }
            public void UseSkill()
            {
                Console.WriteLine("眩晕状态,不能使用技能");
            }
            public void ChangeState()
            {
                champion.State = new Normal(champion);
                champion.State.ChangeState();
            }
        }

        public class Silence : IState
        {
            private Champion champion;
            public Silence(Champion champion)
            {
                this.champion = champion;
            }
            public void Move()
            {
                Console.WriteLine("沉默状态,移动");
            }
            public void Attack()
            {
                Console.WriteLine("沉默状态,攻击");
            }
            public void UseSkill()
            {
                Console.WriteLine("沉默状态,不能使用技能");
            }
            public void ChangeState()
            {
                champion.State = new Normal(champion);
                champion.State.ChangeState();
            }
        }

        public class Dead : IState
        {
            private Champion champion;
            public Dead(Champion champion)
            {
                this.champion = champion;
            }
            public void Move()
            {
                Console.WriteLine("你已经死亡,不能移动");
            }
            public void Attack()
            {
                Console.WriteLine("你已经死亡,不能攻击");
            }
            public void UseSkill()
            {
                Console.WriteLine("你已经死亡,不能使用技能");
            }
            public void ChangeState()
            {
                champion.State = new Normal(champion);
                champion.State.ChangeState();
            }
        }

        static void Main(string[] args)
        {
            var champion = new Champion();

            champion.ChangeState();
            champion.Move();
            champion.Attack();
            champion.UseSkill();

            champion.ChangeState();
            champion.Move();
            champion.Attack();
            champion.UseSkill();

            champion.ChangeState();
            champion.Move();
            champion.Attack();
            champion.UseSkill();

            champion.ChangeState();
            champion.Move();
            champion.Attack();
            champion.UseSkill();

            champion.ChangeState();
            champion.Move();
            champion.Attack();
            champion.UseSkill();
        }
    }
}

运行结果

发布了27 篇原创文章 · 获赞 41 · 访问量 2067

猜你喜欢

转载自blog.csdn.net/DIAX_/article/details/104352868