## State > 讓物件的外顯行為隨內部狀態的改變而改變,彷彿連類別也變了似的 ### 時機 * 當物件行為取決於它的狀態,連執行期行為也得隨狀態而改變時。 * 若是某些操作內,依據物件狀態而做的條件判斷式太過龐大時。 ### 結構 ![](https://i.imgur.com/vvpVmMc.png) ### 參與者 * Context -制定外界改興趣的介面 -持有一個ConcreteState子類別的物件個體,代表現行狀態 * State -此介面負責封裝當Context處於特定狀態時所該展現的行為 * ConcreteState -每一個子類別都針對某一種Context狀態實作出該展現的行為 ```ruby= class Context attr_accessor :state private :state def initialize(state) transition_to(state) end def transition_to(state) puts "Context: Transition to #{state.class}" @state = state @state.context = self end def request1 @state.handle1 end def request2 @state.handle2 end end class State attr_accessor :context def handle1 raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end def handle2 raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end end class ConcreteStateA < State def handle1 puts 'ConcreteStateA handles request1.' puts 'ConcreteStateA wants to change the state of the context.' @context.transition_to(ConcreteStateB.new) end def handle2 puts 'ConcreteStateA handles request2.' end end class ConcreteStateB < State def handle1 puts 'ConcreteStateB handles request1.' end def handle2 puts 'ConcreteStateB handles request2.' puts 'ConcreteStateB wants to change the state of the context.' @context.transition_to(ConcreteStateA.new) end end # The client code. context = Context.new(ConcreteStateA.new) context.request1 context.request2 ``` ### 合作方式 * Context將與狀態有關的訊息委託給目前持的ConcreteState物件去處理 * Context把自己列為參數,傳給處理訊息的State物件,以便後者在必要時能存取得到自己 * Context是主要的對外窗口。客戶碼可用State物件來設定Context組態,設定好後,就不在直接插手State物件 * 可令Context或者ConcreteState的子類別決定在什麼情況下該切換成什麼狀態 ### 優點 1. 集中處理與狀態相依的行為並予以切割(易於新增狀態)。也可以使用資料值的方式儲存,但會造成許多判斷式。 2. 凸顯狀態轉移邏輯。以Context的角度來看,狀態轉移應該是不可再細分的atomic動作;觀念上應該是重設**一個**變數(Context的State物件變數),不該是好幾個變數設來設去。 3. 狀態物件可以共用。如果State物件不含個體變數,Context就可共用State物件(此時State物件就相當於沒有內在狀態或者說只有行為的Flyweight物件)