設計模式-狀態模式(State)

講故事

彩虹環繞這秋香、春香、夏香,秋香、春香、夏香環繞着我,這時鍵盤飛入個人懷中(小朋友,你是否有許多的問號),飛速的敲擊鍵盤,不一下子婀娜多姿的冬香也出如今個人面前。html

有了四大美人相伴,那個人生活要好好從新安排一下,早上誰來服侍我,中午誰來服侍我,下午誰來服侍我,晚上誰來服侍我,想一想都美啊~git

安排...app

Coding

TimeWeather 時間段和天氣枚舉this

/// <summary>
    /// 時間段
    /// </summary>
    public enum Time
    {
        Morning,
        Noon,
        Afternoon,
        Night
    }

    /// <summary>
    /// 天氣
    /// </summary>
    public enum Weather
    {
        /// <summary>
        /// 好天氣
        /// </summary>
        Good,

        /// <summary>
        /// 壞天氣
        /// </summary>
        Bad
    }

Life類,模擬一天的生活,根據不一樣時間段來判斷誰來服侍我幹什麼code

/// <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睡覺中...");
            }
        }
    }

客戶端 開始我一天的生活htm

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

結果展現:對象

如今是 早上,春香 服侍我起牀~

如今是 中午,夏香 陪我玩耍~
天氣真好,陪我出去放風箏

如今是 下午,秋香 服侍我用餐~

如今是 晚上,冬香 服侍我就寢~

結果仍是挺美好的,可是 這個實現過程非常糟糕。blog

尤爲是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

相關文章
相關標籤/搜索