一個菜鳥的設計模式之旅,使用 Golang 實現。本程式實現狀態模式。一個玩家從滿血到死亡的過程有多個狀態,假定健康、輕傷、重傷、死亡狀態,當玩家扣血時判斷切換狀態,更改對象的行為。 ...
一個菜鳥的設計模式之旅,文章可能會有不對的地方,懇請大佬指出錯誤。
編程旅途是漫長遙遠的,在不同時刻有不同的感悟,本文會一直更新下去。
程式介紹
本程式實現狀態模式。一個玩家從滿血到死亡的過程有多個狀態,假定健康、輕傷、重傷、死亡狀態,當玩家扣血時判斷並切換狀態,觸發不同狀態的行為。
健康狀態 HP 90
健康狀態->輕傷狀態
輕傷狀態 HP 70
輕傷狀態->重傷狀態
重傷狀態 HP 50
重傷狀態 HP 30
重傷狀態 HP 10
重傷狀態->死亡狀態
玩家已死亡
強制玩家複活
健康狀態->輕傷狀態
輕傷狀態->重傷狀態
重傷狀態->死亡狀態
玩家已死亡
強制玩家複活
程式代碼
statePattern.go
package main
type ICharacter interface {
DecreaseHP(hp float64)
Respawn()
}
type Player struct {
*Life
}
func (p *Player) DecreaseHP(hp float64) {
p.Life.HP -= hp
p.Life.Request()
}
func (p *Player) Respawn() {
p.Life.HP = 100
p.Life.Request()
}
type ILifeState interface {
Handle(c *Life)
}
type Life struct {
state ILifeState
HP float64
}
func (c *Life) SetState(state ILifeState) {
c.state = state
}
func (c *Life) Request() {
c.state.Handle(c)
}
lifeStates.go
package main
import "fmt"
type HighHP struct{}
type MiddleHP struct{}
type LowHP struct{}
type ZeroHP struct{}
func (hp HighHP) Handle(Life *Life) {
if Life.HP > 80 {
fmt.Println("健康狀態 HP", Life.HP)
} else {
fmt.Println("健康狀態->輕傷狀態")
Life.SetState(&MiddleHP{})
Life.Request()
}
}
func (hp MiddleHP) Handle(Life *Life) {
if Life.HP > 50 {
fmt.Println("輕傷狀態 HP", Life.HP)
} else {
fmt.Println("輕傷狀態->重傷狀態")
Life.SetState(&LowHP{})
Life.Request()
}
}
func (hp LowHP) Handle(Life *Life) {
if Life.HP > 0 {
fmt.Println("重傷狀態 HP", Life.HP)
} else {
fmt.Println("重傷狀態->死亡狀態")
Life.SetState(&ZeroHP{})
Life.Request()
}
}
func (hp ZeroHP) Handle(Life *Life) {
if Life.HP <= 0 {
fmt.Println("玩家已死亡")
} else {
fmt.Println("強制玩家複活")
Life.SetState(&HighHP{})
}
}
main.go
package main
func main() {
p1 := Player{&Life{HP: 100, state: &HighHP{}}}
p1.DecreaseHP(10)
p1.DecreaseHP(20)
p1.DecreaseHP(20)
p1.DecreaseHP(20)
p1.DecreaseHP(20)
p1.DecreaseHP(20)
p1.Respawn()
p1.DecreaseHP(999)
p1.Respawn()
}
Console
健康狀態 HP 90
健康狀態->輕傷狀態
輕傷狀態 HP 70
輕傷狀態->重傷狀態
重傷狀態 HP 50
重傷狀態 HP 30
重傷狀態 HP 10
重傷狀態->死亡狀態
玩家已死亡
強制玩家複活
健康狀態->輕傷狀態
輕傷狀態->重傷狀態
重傷狀態->死亡狀態
玩家已死亡
強制玩家複活
思考總結
什麼是狀態模式
狀態模式(State Pattern)中,類的行為是基於它的狀態改變的。這種類型的設計模式屬於行為型模式。
狀態模式中,我們創建表示各種狀態的對象和一個行為隨著狀態對象改變而改變的 context 對象。
狀態模式:允許對象(Context)在內部狀態發生改變時改變它的行為,對象看起來好像修改了它的類。
如果不用狀態模式,在多分支判斷調用各個行為導致方法過長,責任過大。面向對象設計其實就是希望做到代碼的責任分解。需要想辦法把各個行為變成一個又一個的類,增加時不會影響其他類。狀態的變化在各自的類中完成。
主要解決:
-
當控制一個對象狀態轉換的條件表達式過於複雜時的,把狀態的判斷邏輯轉移到不同狀態的一系列類當中。
-
對象的行為依賴於它的狀態(屬性),並且可以根據它的狀態改變而改變它的相關行為。
何時使用:代碼中包含大量與對象狀態有關的條件語句。
如何解決:將各種具體的狀態類抽象出來。
應用實例:
- 打籃球的時候運動員可以有正常狀態、不正常狀態和超常狀態。
優點:
- 封裝了轉換規則,狀態轉移是在各個具體狀態中進行的,用戶無需知道狀態轉移過程。
- 枚舉可能的狀態,在枚舉狀態之前需要確定狀態種類。
- 將所有與某個狀態有關的行為放到一個類中,並且可以方便地增加新的狀態,只需要改變對象狀態即可改變對象的行為。
- 允許狀態轉換邏輯與狀態對象合成一體,而不是某一個巨大的條件語句塊。
- 可以讓多個環境對象共用一個狀態對象,從而減少系統中對象的個數。
缺點:
- 狀態模式的使用必然會增加系統類和對象的個數。
- 狀態模式的結構與實現都較為複雜,如果使用不當將導致程式結構和代碼的混亂。
- 狀態模式對"開閉原則"的支持並不太好,對於可以切換狀態的狀態模式,增加新的狀態類需要修改那些負責狀態轉換的源代碼,否則無法切換到新增狀態,而且修改某個狀態類的行為也需修改對應類的源代碼。
使用場景:
- 行為隨狀態改變而改變的場景。
- 條件、分支語句的代替者。
註意事項:在行為受狀態約束的時候使用狀態模式,而且狀態不超過 5 個。
參考資料
- 《Go語言核心編程》李文塔
- 《Go語言高級編程》柴樹彬、曹春輝
- 《大話設計模式》程傑
- 單例模式 | 菜鳥教程