2013-08-07 190 views
2

我正在爲我的應用程序實現Java中的狀態模式,並且需要很少的說明。實現狀態模式

狀態機有5個狀態狀態1到狀態5. 總共有5個事件(Event1到Event5)導致狀態轉換。 並非所有事件都適用於所有州。如果事件不適用於該特定狀態,應用程序將拋出異​​常。

當狀態機得到初始化時,它從狀態1開始。

以下是接口和上下文類。

/* 
Interface defining the possible events in each state. 
Each Implementer will handle event in a different manner. 
*/ 
public interface State { 
/* 
    Handlers for each event. Each Implementer will handle the vent in a different manner. 
*/ 
public void handleEvent1(StateContext context); 
public void handleEvent2(StateContext context); 
public void handleEvent3(StateContext context); 
public void handleEvent4(StateContext context); 
public void handleEvent5(StateContext context); 
// Method to enter state and do some action. 
public void enter(StateContext context); 
// Method to exit state and do some clean-up activity on exit . 
public void exit(StateContext context); 
} 

/* 
    Context class which will handle the state change and delegate event to appropriate event handler of current state 
*/ 
Class StateContext { 

    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); 

    private State currentState = null; 

    StateContext() { 
     currentState = new State1(); 
    } 

    //Handle event1 and pass it to the appropriate event handler for the current state. 
    public void handleEvent1() { 
     currentState.handleEvent1(); 
    } 
     . 
     . 
     . 
    //Handle event5 and pass it to the appropriate event handler for the current state. 
    public void handleEvent5() { 
     currentState.handleEvent5(); 
    } 

    // Method to change the state. 
    // This method will be called by each state when it needs to transit to a new state. 
    public void changeState(State newState) { 
      accquireLock(); 
      currentState.exit(); 
      currentState = newState; 
      currentState.enter();   
    } 

    // Release read lock and accquire write lock 
    public void accquireLock() { 
     lock.readLock().unlock() 
     lock.writeLock().lock(); 
    } 

    // accquire readlock and release write lock 
    public void releaseLock() { 
     lock.readLock().lock() 
     lock.writeLock().unlock(); 
    } 
} 

爲了簡單起見,我提供了只有一個狀態的實現。

public class State1 implements State { 
     public void handleEvent1(StateContext context) { 
      //Hand1e Event 1 
     } 
       . 
       . 
       . 
     public void handleEvent5(StateContext context) { 
      //Handle Event 5 
     } 


     public void enter(StateContext context) { 
      //Release the lock here 
      context.releaseLock(); 
      /*Here is my question. Is it a good java practice to expose accquire and release lock in Context object. And use the exposed method here to release lock. 
      */ 

      // Do some action on entering the state. This may take few seconds to finish 

     } 
} 

我想在進入狀態後才能釋放鎖。我也不想鎖定,直到enter()結束。如果我持有鎖直到輸入完成,我不能處理其他事件,並且它可能會超時。對於某些事件(它並不真正改變狀態),我們需要讀取狀態並根據狀態來處理它們或別理他們。如果我沒有釋放鎖定,它們將不能被處理。另外在其他一些情況下,如果一個事件發生關閉(這個事件改變了狀態),當enter()正在進行時,狀態機將無法處理它。我必須立即關閉狀態機,因爲在關機事件發生後繼續輸入()不適合。

我的問題: 這是一個很好的java編程實踐,將accquireLock和releaseLock公開爲Context類中的API並在每個狀態類中使用它們。

感謝, 阿倫

+0

爲什麼在代碼在你的'changeState()'方法中執行後你不調用'releaseLock()'?既然你在該方法開始時獲得了鎖,那麼在它的最後釋放鎖似乎是一致的設計,不是嗎? – Deactivator2

+0

對於我的應用程序,如果我在enter()進行時收到關閉機器的關機事件,我不應該繼續輸入()。繼續可能會導致不利影響,或者可能不會導致任何結果,除非浪費資源直到enter()結束。在某些情況下,輸入可能需要超過5分鐘,併發送很多請求。如果我繼續請求,可能無法到達接收方,或者接收方可能會丟棄我的請求,我應該立即停止並轉到初始狀態(爲此我需要鎖定)。 – Arun

+0

然後,你需要的是一種中斷狀態的'enter'方法的方法,它超越了你已有的任何鎖定。將鎖對象視爲狀態更改的塊,但關閉事件優於任何狀態更改事件,因此應該能夠隨時生效。 – Deactivator2

回答

0

要回答這個問題,如果鎖是國家「經理」班舉行,那麼該類應該在一個控制訪問鎖定,沒有任何實際狀態類的。

關於您持有鎖的聲明,直到enter完成,這正是鎖的關鍵:您不希望其他方法涉及或中斷。相反,您應該開發某種事件隊列來捕獲所有事件,並等待接收對象繁忙時分發它們。要麼是這樣,要麼針對您知道要中斷的事件做出特定例外,以便在指定的事件被觸發時繞過鎖定。

如果您選擇使用中斷方法,則需要在每個State類的enter方法中執行多次檢查,以檢查是否觸發了關閉事件。我看到它唯一的工作方式是讓每個State擴展Thread,以便您可以隨意中斷/停止,儘管在這種情況下聽起來不是一個有效的選項。

+0

我同意你的觀點,如果經理拿着鎖,只有他應該釋放鎖。將獲取和釋放鎖暴露爲管理器中的API並在狀態處理程序中獲取並釋放它是一種很好的做法。例如在State1類中,事件處理程序就像public void handleEvent1(){context.accquireLock(); //做一點事; context.releaseLock(); } – Arun

+0

@Arun是的,這是一個很好的設計模式。另一種方法是將鎖碼置於上下文的事件處理程序中,而不是狀態,但功能相同。 – Deactivator2

+1

好的謝謝...那麼我的設計是在進入enter()方法之後產生一個新的線程來執行進入每個狀態後需要完成的操作。通過這樣做,鎖定將不會被保留,直到操作完成,我可以處理需要只讀取當前狀態的事件。在完成操作之後,即在新產生的線程結束時獲取鎖定改變狀態(如果需要的話)。 – Arun