欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-06-14 23:30:59
...

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

问题背景

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

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

解决方案

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

效果

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

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