設計模式之狀態模式

2021-10-09 21:33:44 字數 4260 閱讀 7646

狀態模式大部分借鑑左蕭龍部落格:

允許物件的內部狀態改變時改變其行為,物件看起來好像修改了它的類。

我們來仔細體會定義中提到的許多要求

前兩點比較好理解,第3點有時候容易給人比較迷惑的感覺,什麼叫這些狀態是可以切換的,而非毫無關係?

舉個例子,比如乙個人的狀態,可以有很多,像生病和健康,這是兩個狀態,這是有關係並且可以轉換的兩個狀態。再比如,睡覺、上班、休息,這也算是一組狀態,這三個狀態也是有關係的並且可以互相轉換。

那如果把生病和休息這兩個狀態放在一起,就顯得毫無意義了。所以這些狀態應該是一組相關並且可互相切換的狀態。

然後我們來看狀態模式中的角色

state:狀態介面,它定義了每乙個狀態的行為集合,這些行為會在context中得以使用。

如果針對剛才對於人的狀態的例子來分析,那麼人(person)就是context,狀態介面依然是狀態介面,而具體的狀態類,則可以是睡覺,上班,休息,這一系列狀態。

我們來試著寫乙個dota的例子,玩過的朋友都知道,dota裡的英雄有很多狀態,比如正常,眩暈,加速,減速等等。相信就算沒有玩過dota的朋友們,在其它遊戲裡也能見到類似的情況。那麼假設我們的dota沒有使用狀態模式,則我們的英雄類會非常複雜和難以維護,我們來看下,原始版的英雄類是怎樣的。

package com.state;

//英雄類

public

class

hero

//停止跑動

public

void

stoprun()

//開始跑動

public

void

startrun()

final hero hero =

this

; runthread =

newthread

(new

runnable()

catch

(interruptedexception e)}}

}); system.out.

println

("--------------開始跑動---------------");

runthread.

start()

;}private

boolean

isrunning()

//英雄類開始奔跑

private

void

run(

)throws interruptedexception

else

if(state == speed_down)

else

if(state == swim)

else

}}

下面我們寫乙個客戶端類,去試圖讓英雄在各種狀態下奔跑一下。

package com.state;

public

class

main

}

可以看到,我們的英雄在跑動過程中隨著狀態的改變,會以不同的狀態進行跑動。

package com.state;

public

inte***ce

runstate

與狀態模式類圖不同的是,我們加入了乙個引數hero(context),這樣做的目的是為了具體的狀態類當達到某乙個條件的時候可以切換上下文的狀態。下面列出四個具體的狀態類,其實就是把if else拆掉放到這幾個類的run方法中。

package com.state;

public

class

commonstate

implements

runstate

}

package com.state;

public

class

speedupstate

implements

runstate

catch

(interruptedexception e)

hero.

setstate

(hero.common)

; system.out.

println

("------加速狀態結束,變為正常狀態------");

}}

package com.state;

public

class

speeddownstate

implements

runstate

catch

(interruptedexception e)

hero.

setstate

(hero.common)

; system.out.

println

("------減速狀態結束,變為正常狀態------");

}}

package com.state;

public

class

swimstate

implements

runstate

catch

(interruptedexception e)

hero.

setstate

(hero.common)

; system.out.

println

("------眩暈狀態結束,變為正常狀態------");

}}

package com.state;

//英雄類

public

class

hero

//停止跑動

public

void

stoprun()

//開始跑動

public

void

startrun()

final hero hero =

this

; runthread =

newthread

(new

runnable()

}});

system.out.

println

("--------------開始跑動---------------");

runthread.

start()

;}private

boolean

isrunning()

}

可以看到,現在我們的英雄類優雅了許多,我們使用剛才同樣的客戶端執行即可得到同樣的結果。

對比我們的原始例子。

現在我們使用狀態模式之後,有幾個明顯的優點:

我們去掉了if else結構,使得**的可維護性更強,不易出錯,這個優點挺明顯,如果試圖讓你更改跑動的方法,是剛才的一堆if else好改,還是分成了若干個具體的狀態類好改呢?答案是顯而易見的。

使用多型代替了條件判斷,這樣我們**的擴充套件性更強,比如要增加一些狀態,假設有加速20%,加速10%,減速10%等等等(這並不是虛構,dota當中是真實存在這些狀態的),會非常的容易。

狀態是可以被共享的,這個在上面的例子當中有體現,看下hero類當中的四個static final變數就知道了,因為狀態類一般是沒有自己的內部狀態的,所有它只是乙個具有行為的物件,因此是可以被共享的。

狀態的轉換更加簡單安全,簡單體現在狀態的分割,因為我們把一堆if else分割成了若干個**段分別放在幾個具體的狀態類當中,所以轉換起來當然更簡單,而且每次轉換的時候我們只需要關注乙個固定的狀態到其他狀態的轉換。安全體現在型別安全,我們設定上下文的狀態時,必須是狀態介面的實現類,而不是原本的乙個整數,這可以杜絕魔數以及不正確的狀態碼。

狀態模式適用於某乙個物件的行為取決於該物件的狀態,並且該物件的狀態會在執行時轉換,又或者有很多的if else判斷,而這些判斷只是因為狀態不同而不斷的切換行為。

狀態模式也有它的缺點,不過它的缺點和大多數模式相似,有兩點。

4. 會增加的類的數量。

5. 使系統的複雜性增加。

儘管狀態模式有著這樣的缺點,但是往往我們犧牲複雜性去換取的高可維護性和擴充套件性是相當值得的,除非增加了複雜性以後,對於後者的提公升會乎其微。

設計模式之狀態模式

一 作用 允許乙個物件在其內部狀態改變時改變它的行為,物件看起來似乎修改了它的類。其別名為狀態物件 objects for states 狀態模式是一種物件行為型模式。二 例子 狀態抽象類 abstract class state 具體狀態類,每個狀態對應乙個類 class concretestat...

設計模式之 狀態模式

gof 設計模式 中給狀態模式下的定義為 允許乙個物件在其內部狀態改變時改變它 的行為。這個物件看起來似乎修改了它的類。看起來,狀態模式好像是神通廣大 居然能夠 修改自身的類 能夠讓程式根據不同的外部情況來做出不同的響應,最直接的方法就是在程式中將這些可能發生的外部情況全部考慮到,使用 if els...

設計模式之狀態模式

定義 允許乙個物件在其內部狀態變化時改變它的行為,物件看起來似乎修改了它所屬的類。類圖 說明 context表示環境類,它定義了客戶應用程式感興趣的介面,並維護乙個concretestate子類的例項,這個例項用於定義當前狀態 state表示抽象狀態類,它定義了乙個介面以封裝與context的乙個特...