Java設計模式之狀態模式(State)

java的設計模式一共有24種,我自己經常用到的是單例模式,觀察者模式,策略模式。我也有去了解建造者模式,工廠模式等,但是看了也就忘了,這就說明了,看了一定要馬上拿來用!項目裏到處都可以進行改造,信手拈來就是用,這樣一定會讓自己很難忘的,下次在合適的契機也能想到用什麼模式。

話不多說,說說我爲什麼需要來研究狀態模式(State)。最近遇到一個需求,兩個相似度很高的界面,(首先遇到這種我就會想能不能用一個界面把它解決完),好的!暫且讓他們用一個界面來實現吧!如果是這樣的話,那麼一個界面就有兩種狀態,一種是A狀態,一種是B狀態。當我點擊A的時候調用A模式,點擊B的時候調用B模式。

思考:涉及到狀態判斷,那麼也就說我需要寫很多的 if else 判斷,(隨後我老老實實的寫了3次判斷在代碼裏,準備第4次的時候,我停下了。感覺自己做的事很蠢,於是我急於尋求一個設計模式來幫我走出困境),然後我晃眼一看,我覺得狀態模式很適合我!

但我在網上看了很多講解狀態模式的博客,不是說看不懂,但就是真的讓人失望,寫得很不走心,很敷衍,講得不明不白的,還有的甚至和策略模式混爲一談,實在是滑稽。

所以我決定自己來寫一篇自己可以隨時拿出來複習的博客,去其糟粕,取其精華,寫這篇博客的目的是能夠讓自己明白爲前提。

注意:本文的示例代碼是用kotlin編寫,語法簡單,不存在高級寫法,易懂!

 

我將從以下幾點來說明狀態模式:

(一).狀態模式適用於哪種情況

(二).如何使用狀態模式

(三).狀態模式的好處

(四).狀態模式的缺點

(五).狀態模式和策略模式的差別

 

一.狀態模式適合用於哪種情況

     狀態模式適用於,當一個對象的行爲取決於它的狀態,並且必須根據它在運行時刻根據狀態改變它的行爲時。這種情況適合使用狀態模式。

上面的說法比較官方,現在我用白話來解釋一下。(不保證每個人都能懂哈)

假設:一個集美貌與才華的美少男,他叫XMan,這個男子給自己安排了一天花樣很多的行程,我們就暫且理解XMan是個日程怪吧。

他早上7:00-9:00,有這麼幾個事要做:洗漱,喫飯,看早報,遛狗。

然後9:00-12:00,他這個時間段,唯一要做的是:在公司認認真真的划水摸魚。

隨後12:00-14:00,他需要一個美容午覺。

下午14:00-16:00,他要和國際大佬鐵柱開會。

下午16:00-18:00,他要做的事是:喝下午茶,寫日報。

晚上18:00-21:00,這個時間點:和XWoman約會。

你要寫一個這樣的XMan類出來,處理他一天要做的這些事,你是不是第一反應是:啊,我要枚舉,我要if,我要else,我還有switch,我還有.....行了,你閉嘴吧。

這些你要是用if else,switch來判斷,你確定不會瘋掉嗎?(不管你會不會,反正我會。)

這種情況下,可以使用狀態模式來做處理。

 

二.如何使用狀態模式

    1.狀態模式需要做什麼

我先來介紹一下,使用狀態模式大概要用到的幾個東西吧。你想要開車,你總得知道這個車是什麼車吧,箱式的,還是小轎車,是自動擋,還是手動擋。

萬一你覺得這個對你來說複雜了,你不樂意玩兒,或者玩不了,就可以趁早換車(換設計模式),不用浪費時間。(多嗶嗶了兩句,下面開始表演)

我們大概會新建以下幾個類:

  XManAllState:抽象狀態

  XManStateController:控制你的狀態的一個控制器

  XManActivity:調用狀態的Activity

  Xman7To9:狀態7:00-9:00的狀態類

  XMan9To12:狀態9:00-12:00的狀態類

  XMan12To14:狀態12:00-14:00的狀態類

....這裏你有幾個狀態,就有幾個狀態類。

我是屬於寧願多寫幾個類,也不願意在一個方法裏寫過多代碼的人,不知道大家還記不記得一句話,一個方法過長,內容過多,你就應該思考你的代碼是不是有壞掉了的味道。

現在你就大概知道狀態模式需要寫什麼類,而這些類又分別是拿來做什麼的,那接下來就是具體的實現了。

2.具體做法(這裏的幾個類都是相互牽扯的,看的時候最好敲一下,否則小菜看完後可能會不明白)

步驟一:新建一個抽象狀態類,裏面有一個抽象方法。(這裏傳的參數是我們的控制器類,以防代碼內部報錯,你可以先建一個控制器空類,這樣可以跟着我一步一步走)

abstract class XManAllState {

    abstract fun XManTimeState(XManController:XManStateController)
}

步驟二:新建一個狀態類,繼承抽象狀態類,實現抽象方法,這裏是7:00-9:00這個時段的狀態。(建議把所有的狀態都先建立好,具體實現後面會一步一步的來)

class Xman7To9:XManAllState(){

    override fun XManTimeState(XManController: XManStateController) {
       
    }

}

步驟三:走到這裏,那麼前面兩個步驟你已經完成了。現在是很重要的一個步驟了,創建XMan日程的控制器類。

class XManStateController {

    //時間的初始化是7-9之間,自己任意設置
    var currentState:XManAllState=Xman7To9()

    //時間在Activity給它賦值,狀態類用它來判斷時間是否滿足狀態
    var Hour:Int = 0

    //設置當前的狀態
    fun XManTimeState(){
        currentState.XManTimeState(this)
    }

}

步驟四:具體實現各個狀態類

上面我們已經把控制器寫好了,現在要做的就是把各個狀態類的具體實現寫出來。我還是以7:00-9:00的狀態來打個樣兒吧。

class Xman7To9:XManAllState(){
    override fun XManTimeState(XManController: XManStateController) {
        //做時間判斷是在7-9之內的
        if (XManController.Hour<9&&XManController.Hour>=7){
            do7To9()
        }else{
            //如果時間不在7-9之內,則修改當前的state爲9-12狀態類
            XManController.currentState=XMan9To12()
        }
    }

    //7-9之內的具體實現方法
    fun do7To9(){
        Log.i("XMan","XMan洗漱,喫飯,看早報,遛狗")
    }
}

接下來我再上一個9:00-12:00狀態類的具體實現。(這樣大家可以多看兩個我的實現類,不會一個看完了還暈乎乎的)

class XMan9To12 :XManAllState(){

    override fun XManTimeState(XManController: XManStateController) {
        //這裏我做個解釋:因爲我的Hour值是int類型,kotlin自動幫我把XManController.Hour>=9&&XManController.Hour<12 
        //這個判斷轉化成 XManController.Hour in 9..11 ,爲什麼是11,因爲在int裏,<12就是11
        if (XManController.Hour in 9..11){
            do9To12()
        }else{
            XManController.currentState=XMan12To14()
        }
    }

    fun do9To12(){
        Log.i("XMan","努力認真的划水摸魚")
    }

}

接下來的的狀態類就按照上面的依次來做就可以了。你可能會想,那我最後一個狀態,也就是18:00-21:00如果時間不滿足,怎麼做呢?

我會給大家一個思路。下面的這個代碼是我對於18:00-21:00狀態的處理。

大家可以看到我對這個狀態的處理就是,只處理了18:00-21:00的時間段。你可以根據自己的需要,對不在這個範圍的情況做自己相應的處理。

class XMan18To21 :XManAllState(){

    override fun XManTimeState(XManController: XManStateController) {
        if (XManController.Hour in 18..20){
            do18To21()
        }
    }

    fun do18To21(){
        Log.i("tang","和XWoman約會")
    }
}

現在我們已經做到這裏了,就只差最後一步了。

步驟五:調用狀態模式。(此處我是在Activity中調用)

class XManActivity :AppCompatActivity(),View.OnClickListener{

    private lateinit var btn_xman_start:Button
    //創建控制器對象
    private var XManControll=XManStateController()
    //當前的時間
    private var currentTime:Int=7 

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.layout_xman)

        btn_xman_start=findViewById(R.id.btn_xman_start)
        btn_xman_start.setOnClickListener(this)
    }

    override fun onClick(v: View?) {
        when(v?.id){
            R.id.btn_xman_start->{
                //設置當前時間
                XManControll.Hour=currentTime
                //調用狀態方法
                XManControll.XManTimeState()
            }
        }
    }
}

到此爲止,整個過程已經完畢了。

我們可以看到,Activity裏關於狀態的控制,我們僅僅用了兩行代碼就實現了,整個過程邏輯清晰,代碼層層分明,沒有大篇幅的if else,switch來做判斷。代碼的可讀性和解耦性都很高。

 

三.狀態模式的好處

       將與特定狀態相關的行爲局部化,並且將不同狀態的行爲分割開來,消除龐大的條件分支語句,狀態模式通過把各種狀態轉移邏輯分佈到Sate的子類之間,來減少彼此之間的依賴,此時就變得容易維護和擴展了。

     (大家多使用幾次也就知道狀態模式的好處了,肉眼可見的好處)

 

四.狀態模式的缺點

       俗話說,人無完人。這句話同理也適用於咱們的狀態模式。其實你如果跟着敲,可能已經能夠深切的感受到它的缺點了。

幾個狀態,就有幾個類來處理。是不是類有點多了?我暫時發現的就是這個缺點,類過多。大家請根據自己的實際情況來酌情使用。

 

五.狀態模式和策略模式的區別

     很多人如果使用過策略模式,你可能第一印象會覺得狀態模式和它很像。的確,而且我看很多博主介紹的狀態模式和具體實現,我發現很多人直接就把策略模式當做狀態模式來使用了。這絕對是個誤區!它倆無論是使用還是實現都完全不是一個東西,切忌不要覺得它倆是一樣的。這也是我爲什麼要寫這一篇博客的原因,想讓大家把這兩種設計模式區分開來!

說到這裏我多提一句,之前我在篩選簡歷的時候,發現有人在簡歷上寫 —— 熟悉設計模式MVC,MVP。

如果你也這樣寫的話,還是快醒醒吧。MVP和MVC,MVVM不是設計模式!這是設計架構!!!

上面多說了一寫,我現在正式的介紹一下狀態模式和策略模式的區別。

狀態模式的類

  XManAllState:抽象狀態(參數是控制器類)----------------XManStrategy:接口(參數是你要在各個策略類中要使用的變量)

  XManStateController:控制你的狀態的一個控制器---------XManCal:寫一個暴露的方法,該方法內調用接口的方法。

  XManActivity:調用狀態的Activity-----------------------------XManActivity:把每一個策略new出來,將new出來的對象傳入XManCal作爲參數。隨後調用XManCal暴露的方法,將變量傳入方法,

  Xman7To9:狀態7:00-9:00的狀態類----------------------------XManStrategy1:策略1

  XMan9To12:狀態9:00-12:00的狀態類----------------------XManStrategy2:策略2

  XMan12To14:狀態12:00-14:00的狀態類-------------------XManStrategy3::策略3

看了上面的對比你可能覺得有點暈乎乎的,沒關係。當你真正要使用策略模式的時候,你看一遍網上的demo也就會了。

我個人覺得狀態模式比策略更難懂一些,策略模式相較很簡單,他們兩者最大的區別就是:它們的具體實現以及一個是抽象類,一個是接口。總而言之,他倆就是完全不同的兩個東西。

 

至此,我對於狀態模式的介紹也就差不多了。不正之處請多指教,如有疑問歡迎提出來,雖然我不一定能解答

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章