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也就會了。
我個人覺得狀態模式比策略更難懂一些,策略模式相較很簡單,他們兩者最大的區別就是:它們的具體實現以及一個是抽象類,一個是接口。總而言之,他倆就是完全不同的兩個東西。
至此,我對於狀態模式的介紹也就差不多了。不正之處請多指教,如有疑問歡迎提出來,雖然我不一定能解答