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

设计模式-状态模式(State)

程序员文章站 2022-03-08 14:14:45
讲故事 彩虹环绕这秋香、春香、夏香,秋香、春香、夏香环绕着我,这时键盘飞入我的怀中( "小朋友,你是否有许多的问号" ),飞速的敲击键盘,不一会儿婀娜多姿的冬香也出现在我的面前。 有了四大美人相伴,那我的生活要好好重新安排一下,早上谁来服侍我,中午谁来服侍我,下午谁来服侍我,晚上谁来服侍我,想想都美 ......

讲故事

彩虹环绕这秋香、春香、夏香,秋香、春香、夏香环绕着我,这时键盘飞入我的怀中(),飞速的敲击键盘,不一会儿婀娜多姿的冬香也出现在我的面前。

有了四大美人相伴,那我的生活要好好重新安排一下,早上谁来服侍我,中午谁来服侍我,下午谁来服侍我,晚上谁来服侍我,想想都美啊~

安排...

coding

timeweather 时间段和天气枚举

    /// <summary>
    /// 时间段
    /// </summary>
    public enum time
    {
        morning,
        noon,
        afternoon,
        night
    }

    /// <summary>
    /// 天气
    /// </summary>
    public enum weather
    {
        /// <summary>
        /// 好天气
        /// </summary>
        good,

        /// <summary>
        /// 坏天气
        /// </summary>
        bad
    }

life类,模拟一天的生活,根据不同时间段来判断谁来服侍我干什么

    /// <summary>
    /// 生活
    /// </summary>
    public class life
    {
        /// <summary>
        /// 时间段
        /// </summary>
        public time time { get; set; }

        /// <summary>
        /// 天气
        /// </summary>
        public weather weather { get; set; }

        /// <summary>
        /// 干啥子
        /// </summary>
        public void doing()
        {
            if (time == time.morning)
            {
                console.writeline($"\n现在是 早上,春香 服侍我起床~");
            }
            else if (time == time.noon)
            {
                console.writeline($"\n现在是 中午,夏香 陪我玩耍~");
                if (weather == weather.good)
                {
                    console.writeline($"天气真好,陪我出去放风筝");
                }
                else
                {
                    console.writeline($"天气不好,待家给我跳舞");
                }
            }
            else if (time == time.afternoon)
            {
                console.writeline($"\n现在是 下午,秋香 服侍我用餐~");
            }
            else if (time == time.night)
            {
                console.writeline($"\n现在是 晚上,冬香 服侍我就寝~");
            }
            else
            {
                console.writeline($"\n睡觉中...");
            }
        }
    }

客户端 开始我一天的生活

    internal class program
    {
        private static void main(string[] args)
        {
            var life = new life();
            //设置天气
            life.weather = weather.good;

            life.time = time.morning;
            life.doing();

            life.time = time.noon;
            life.doing();

            life.time = time.afternoon;
            life.doing();

            life.time = time.night;
            life.doing();

            console.writeline("\nhappy ending~");
            console.readline();
        }
    }

结果展示:

现在是 早上,春香 服侍我起床~

现在是 中午,夏香 陪我玩耍~
天气真好,陪我出去放风筝

现在是 下午,秋香 服侍我用餐~

现在是 晚上,冬香 服侍我就寝~

结果还是挺美好的,但是 这个实现过程很是糟糕。

尤其是life.doing()

        /// <summary>
        /// 干啥子
        /// </summary>
        public void doing()
        {
            if (time == time.morning)
            {
                console.writeline($"\n现在是 早上,春香 服侍我起床~");
            }
            else if (time == time.noon)
            {
                console.writeline($"\n现在是 中午,夏香 陪我玩耍~");
                if (weather == weather.good)
                {
                    console.writeline($"天气真好,陪我出去放风筝");
                }
                else
                {
                    console.writeline($"天气不好,待家给我跳舞");
                }
            }
            else if (time == time.afternoon)
            {
                console.writeline($"\n现在是 下午,秋香 服侍我用餐~");
            }
            else if (time == time.night)
            {
                console.writeline($"\n现在是 晚上,冬香 服侍我就寝~");
            }
            else
            {
                console.writeline($"\n睡觉中...");
            }
        }

这个方法存在以下问题:

  1. 方法很长(如果我们再把时间段细分以下,或者每个时间段做的事情增加。实际开发中,可能每个判断中包含很多很复杂的业务逻辑)。开发过程中,我们要尽量保持每个方法体代码行数不超过50行。
  2. 判断分支太多。if else很多,并且还存在嵌套,该方法的责任过大,同时也不方便阅读。
  3. 维护困难。以后要增加时间段,比如我 想在深夜 有人来服侍我吃宵夜,要修改life.doing()来实现,这完全违背了我的的第3条!

如何解决上面的问题呢?这时房中升起了四个金光闪闪的大字——状态模式

状态模式

敲黑板·划重点

定义: 当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。(上面案例中,时间段time就是life对象的状态,当time改变时,life的行为doing()就改变了)

作用: 主要解决当控制一个对象状态转换条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。

好处: 将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。

思想: 将特定状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于具体的状态对象中,所以通过定义新的子类就可以很容易地增加新的状态和转换。消除庞大的条件分支语句,通过把各种状态转移逻辑分布到状态的子类之间,来减少相互间的依赖。

解决方法已经找到,就开始干吧...

code upgrade

itimehandle接口,定义各种时间段(状态)的行为。

    public interface itimehandle
    {
        /// <summary>
        /// 干啥子
        /// </summary>
        /// <param name="life"></param>
        public void doing(life life);
    }

life类,属性中包含itimehandle,行为doing()交个具体的时间段操作(状态对象)。

    /// <summary>
    /// 生活
    /// </summary>
    public class life
    {
        private itimehandle _timehandle;

        /// <summary>
        /// 时间段
        /// </summary>
        public time time { get; set; }

        /// <summary>
        /// 天气
        /// </summary>
        public weather weather { get; set; }

        /// <summary>
        /// 初始化 一天生活的 时间段
        /// </summary>
        public life()
        {
            //默认设置时间为 上午
            _timehandle = new morningtime();
        }

        /// <summary>
        /// 设置时间段对应的操作类
        /// </summary>
        /// <param name="timehandle"></param>
        public void settimehandle(itimehandle timehandle)
        {
            _timehandle = timehandle;
        }

        /// <summary>
        /// 干啥子
        /// </summary>
        public void doing()
        {
            _timehandle.doing(this);
        }
    }

morningtimenoontimeafternoontimenighttime类实现itimehandle接口(这也就是 思想 中:将与特定状态相关的行为局部化。所有与状态相关的代码都存在于具体的状态对象中)。在具体的时间段类中来判断改该做什么(这也就是 作用 中:把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化)。

    /// <summary>
    /// 早上 干什么
    /// </summary>
    public class morningtime : itimehandle
    {
        public void doing(life life)
        {
            if (life.time == time.morning)
            {
                console.writeline($"现在是 早上,春香 服侍我起床~");
            }
            else
            {
                life.settimehandle(new noontime());
                life.doing();
            }
        }
    }

    /// <summary>
    /// 中午 干什么
    /// </summary>
    public class noontime : itimehandle
    {
        public void doing(life life)
        {
            if (life.time == time.noon)
            {
                console.writeline($"现在是 中午,夏香 陪我玩耍~");
                if (life.weather == weather.good)
                {
                    console.writeline($"天气真好,陪我出去放风筝");
                }
                else
                {
                    console.writeline($"天气不好,待家给我跳舞");
                }
            }
            else
            {
                life.settimehandle(new afternoontime());
                life.doing();
            }
        }
    }

    /// <summary>
    /// 下午 干什么
    /// </summary>
    public class afternoontime : itimehandle
    {
        public void doing(life life)
        {
            if (life.time == time.afternoon)
            {
                console.writeline($"现在是 下午,秋香 服侍我用餐~");
            }
            else
            {
                life.settimehandle(new nighttime());
                life.doing();
            }
        }
    }

    /// <summary>
    /// 晚上 干什么
    /// </summary>
    public class nighttime : itimehandle
    {
        public void doing(life life)
        {
            if (life.time == time.night)
            {
                console.writeline($"现在是 晚上,冬香 服侍我就寝~");
            }
            else
            {
                console.writeline($"睡觉中...");
            }
        }
    }

客户端代码 和 输出结果 同上

现在,我们再来看看life.doing()

    /// <summary>
    /// 干啥子
    /// </summary>
    public void doing()
    {
        _timehandle.doing(this);
    }

方法简洁明了,消除了过多的判断分支。

同时life类的职责简化了,做到代码的责任分解。符合了我的第1条和第3条。

这时候,我们再要增加一个深夜吃夜宵的时间段,life类就不需要动了。我们只需增加weehourstime类实现itimehandle,调整nighttime中的判断就可以了。

吟诗

糟糕的问题完美解决,我心甚欢,吟诗一首:

桃花坞里桃花庵,

桃花庵下桃花仙。

桃花仙人敲代码,

桃花运来年复年。

示例代码地址: https://gitee.com/sayook/designmode/tree/master/state