網頁

2012年9月30日 星期日

State Pattern 狀態模式

今天想跟大家分享的設計模式叫做”State Pattern” 狀態模式,最近在工作上遇到一個案例,我覺得就非常適合使用這個模式去重構,當我今天看到在某個物件中,有過多的if….else if…..or Switch case…或許你可以試著思考是否可以使用State Pattern去把過多的條件敘述簡化掉,在我遇到的案例中,我發現了像下列的程式碼:

  1: void Update()
  2: {
  3:     switch (PlayerMode)
  4:     {
  5:         case : Attack
  6:         //....
  7:         break;
  8:         
  9:         case : Idle
 10:         //....
 11:         break;
 12:         
 13:         case : Damage
 14:         //....
 15:         break;
 16:         
 17:         case : Move
 18:         //....
 19:         break;
 20: 
 21:         case : Jump
 22:         //....
 23:         break;
 24: 
 25:         case : JumpAttack
 26:         //....
 27:         break;
 28: 
 29:         case : Run
 30:         //....
 31:         break;
 32:        //.....................
 33: 
 34:     }
 35: }



這時候我們會發現所有的條件判斷可能都在這一個Update Function內,當我們需要增加新的狀態的時候,我們就必須增加新的程式碼在這個Function裡面,然後可能每個Switch case判斷敘述中,又會加很多在該狀態才會特有的敘述,這些對程式碼的閱讀以後後續的維護,都相對的來的複雜了些,所以今天我們來使用”State Pattern”


State Pattern的定義:


允許物件隨著內在的狀態改變而改變行為,好像物件的類別改變了一樣。

這句話是什麼意思呢,還記得我們上一篇談到的策略模式嗎。當我們需要改變他的行為的時候就去替換該行為的演算法,在State Pattern中,也是運用一樣的概念,根據狀態改變,而改變行為的演算法。


 


類別圖:

clip_image001

以上圖來看,狀態模式允許一個物件能夠根據內部狀態的不同而有不同的行為。

藉由將每個狀態都封裝成各自的物件,以後任何改變的需求都將只會影響到局部。

所以,以案例來看,我們的程式碼可能就會變成:

  1: class PlayerState 
  2: {
  3: public:
  4:     PlayerState();
  5:     virtual ~PlayerState();
  6:     virtual void Update();
  7: }
  8: 
  9: class Attatck : public PlayerState 
 10: {
 11: public:
 12:     Attatck ();
 13:     virtual Attatck ();
 14:     virtual void Update();
 15: }
 16: 
 17: class Idle: public PlayerState 
 18: {
 19: public:
 20:     Idle: ();
 21:     virtual Idle: ();
 22:     virtual void Update();
 23: }
 24: ///.........
 25: ///..........
 26: ///..........
 27: 
 28: void Update()
 29: {
 30:     m_pCurrentState->Update()
 31: }
 32: 
 33: void ChangeState(PlayerMode)
 34: {
 35:     switch case:
 36:     {
 37:         case Attatk:
 38:         m_pCurrentState = m_pAttackState;
 39:         break;
 40:     }
 41: }
類似這樣的概念,我們還可以使用一個類別,去管理你所有的State,可能會有一個list or Vector 容器,去做儲存所有State,然後再切換的時候根據目前的狀況,去更換所使用的類別,不過這些不是這邊討論的範圍。

狀態模式與策略模式:

剛剛我們有談到,狀態模式跟策略模式,都是根據需求不同,去切換封裝的演算法。但是他們還是有些些微的差異。


以狀態模式而言,我們將一群行為封裝在狀態物件中,context的行為隨時可以委派到那些狀態物件的其中之一。隨著時間的改變,目前狀態將持續改變所有狀態物件的行為,反映出context內部的狀態,因此,context的行為也跟著改變,但是context的user對於狀態物件所知不多,

以策略模式而言,user通常主動指定context所要合成的策略物件為何者。現在,固然策略模式具有彈性,能夠動態在執行期做改變,但對於某個context來說,經常都只有一個最適當的策略物件。

 

但是狀態模式跟策略模式都有一樣的缺點,當你的State越來越多,相對的你的class就會越來越多,這是因為你需要分割封裝的演算法變多了,相對的class就多了。

 

沒有留言:

張貼留言