設計之禪 狀態模式

2021-09-10 15:05:34 字數 3779 閱讀 6366

之前我寫過一篇策略模式的文章,講的是如何靈活地改變物件的行為,今天要講的模式和策略模式非常像,它也是讓你設計出如何靈活改變物件行為的乙個模式,與策略模式不同的是它是根據自身狀態而自行地改變行為,它就是狀態模式。

首先我們來分析乙個例項:現在的遊戲基本都有自動打怪做任務的功能,如果讓你實現這個功能你會怎麼做呢?

本篇講解的是狀態模式,當然首先應該分析其應有狀態和行為,下面是我畫的乙個簡單的狀態圖:

橢圓代表的是所處狀態,指引線代表執行的行為。一開始角色處於初始狀態,什麼也不做,當玩家開啟自動任務功能時,角色就自動的接受任務,當接到殺怪的任務後,發現周圍沒有怪,就把「初始狀態」改為「未發現怪物」狀態並開始四處遊走尋找怪物,走啊走,走啊走,發現了目標怪物就將狀態修改為「發現怪物」,然後開始攻擊打怪,直到殺怪數量達到任務指定數量後,就停止打怪並將狀態修改為「任務達成」狀態,最後回到接任務那裡提交任務,角色狀態又重置為初始狀態(這裡只是為了方便理解該模式,不要太糾結功能細節)。不難發現,在該例項中,我們包含了四個狀態和四個行為,任何乙個行為是隨時都有可能進行的,但是其表現結果卻會因為狀態的不同而有不一樣的結果,按照我們面向過程的程式設計方式也是非常容易實現的:

public

class

character

else

if(state == hasmonster)

else

if(state == nomonster)

else

if(state == missionclear)

}private

void

move()

else

if(state == hasmonster)

else

if(state == nomonster)

else

if(state == missionclear)

}private

void

attack()

private

void

submit()

}

最後兩個方法我沒有給出具體實現,相信難不倒你,當全部實現後角色就能自動接任務打怪了:

accept the task.need to kill monster:

10moving to find monster

need to kill:

9need to kill:

8need to kill:

7need to kill:

6need to kill:

5need to kill:

4need to kill:

3need to kill:

2need to kill:

1need to kill:

0moving to submit

congratulations on completing the task!

不過,功能雖然實現了,但是這樣寫**冗長不說,還非常難於理解維護,想象一下這裡只假設了4種狀態,當如果有非常多的狀態,那就是滿篇的if else了,而且如果未來需要增加新的狀態,那麼當前的實現無疑是違反了open-close原則的,我們沒有封裝變化的那部分。那應該如何做呢?這就需要我們的狀態模式了。

往下看之前,不妨先仔細思考一下,既然該功能中狀態是會隨時改變的,而行為又會受到狀態的影響,那何不將狀態抽離出來成為乙個體系呢?比如定義乙個狀態介面(為什麼這裡需要定義所有的行為方法呢?):

public

inte***ce

state

那麼角色類中就可以如下定義了:

public

class

character

public

void

move()

public

void

attack()

public

void

submit()

public

void

killone()

public

void

setcurrent

(state current)

public

void

setcount

(int count)

public state getcurrent()

public

intgetcount()

}

相比較之前,新的類只保留了當前狀態,並增加了getter和setter方法,而角色的行為則全都委託給了具體的狀態類來實現,那具體的狀態類應該如何實現呢?

// 初始狀態

public

class

stopstate

implements

state

@override

public

void

accept

(int count)

@override

public

void

move()

@override

public

void

attack()

@override

public

void

submit()

}// 附近沒有怪物

public

class

nomonsterstate

implements

state

@override

public

void

accept

(int count)

@override

public

void

move()

@override

public

void

attack()

@override

public

void

submit()

}

這裡我也只給出了兩個實現類,其它的相信你能很容實現它們。通過狀態模式重構後,**清晰了很多,沒有滿屏的if else,角色也能夠根據當前所處的狀態表現出相應的行為,同時如果需要增加新的狀態時,只需要實現state介面就行了,看起來相當完美。但是,沒有什麼模式是完美的,使用狀態模式的缺點我們很容易發現,原來乙個類就能解決的,現在裂變為了四個類,系統結構複雜了很多,但這樣的犧牲是非常有必要和值得的。

剛剛我們已經實現了狀態模式,但是還有個細節問題不知你注意到了沒有?比如:

public

void

move()

c.

setcurrent

(c.gethasmonsterstate()

);

對此我有點疑問,即使使用getter獲取,那未來系統進化導致狀態的改變後難道不需要修改getter方法名麼?

狀態模式允許物件在內部狀態改變時改變它的行為,如果需要在多個物件間共享狀態,那麼只需要定義靜態域即可。

狀態模式與策略模式具有相同的類圖,但它們本質的意圖是不同的。前者是封裝基於狀態的行為,並將行為委託到當前的狀態,使用者不需要知道有哪些狀態;而後者是將可以互換的行為封裝起來,然後使用委託,由客戶決定需要使用哪種行為,客戶需要知道所有的行為類。

設計模式之禪 狀態模式

類圖比較通俗易懂,那麼先來實現一下,看看有什麼問題 看到這個程式,是不是有點太簡單 非也非也,繼續往下,這個程式有什麼問題?電梯門可以開啟,但是不能隨便開啟吧!電梯執行這四個動作應該都有前提條件,在特定的條件下才能做特定的動作 為了解決這些暫時能想到的問題,先來分析電梯有哪些特定的狀態 電梯的狀態和...

設計模式之禪

設計模式之禪 大話面向初學者 禪面向有了一定基礎後提公升能力的讀者 看大話,只是看故事,只是感性認識,對於很多初學者而又沒專案經驗 或 閱讀 編寫量 的人來說,比較適合用於入門 看禪 主要是有一定的專案經驗 或 閱讀 編寫量 基礎上,而又大致閱讀過23種設計模式中的20種以上基本概念後,再深化提公升...

《設計模式之禪》讀書筆記 21 狀態模式

定義 當乙個物件內在狀態改變時允許其改變行為,這個物件看起來像改變了其類。類圖 state抽象狀態角色 介面或抽象類,負責物件狀態定義,並且封裝環境角色以實現狀態切換。concretestate具體狀態角色 每乙個具體狀態必須完成兩個職責 本狀態的行為管理以及趨向狀態處理,通俗地說,就是本狀態下要做...