簡體   English   中英

java中的高效狀態機模式

[英]Efficient state machine pattern in java

我正在編寫一個java模擬應用程序,它有很多實體可供模擬。 這些實體中的每一個在系統中的任何時間都具有某種狀態。 對這樣的實體進行建模的可能且自然的方法是使用狀態(或狀態機)模式。 問題是如果有很多狀態切換,它會在運行時創建很多對象,這可能會導致系統性能下降。 我有哪些設計選擇? 我希望性能成為可維護性之后的主要標准。

謝謝

以下代碼將為您提供高性能(~10ns / event)零運行時GC狀態機實現。 只要系統或組件中有狀態概念,就可以使用顯式狀態機,這不僅可以使代碼保持清潔和可擴展,還可以讓人們(甚至程序員)立即看到系統的功能,而無需深入研究多個回調:

abstract class Machine {
    enum State {
      ERROR,
      INITIAL,
      STATE_0,
      STATE_1,
      STATE_2;
    }

    enum Event {
      EVENT_0,
      EVENT_1,
      EVENT_2;
    }

    public static final int[][] fsm;
    static {
      fsm = new int[State.values().length][];
      for (State s: State.values()) {
        fsm[s.ordinal()] = new int[Event.values().length];
      }
    }

    protected State state = State.INITIAL;
    // child class constructor example
    // public Machine() {
    //   // specify allowed transitions
    //   fsm[State.INITIAL.ordinal()][Event.EVENT_0.ordinal()] = State.STATE_0.ordinal();
    //   fsm[State.STATE_0.ordinal()][Event.EVENT_0.ordinal()] = State.STATE_0.ordinal();
    //   fsm[State.STATE_0.ordinal()][Event.EVENT_1.ordinal()] = State.STATE_1.ordinal();
    //   fsm[State.STATE_1.ordinal()][Event.EVENT_1.ordinal()] = State.STATE_1.ordinal();
    //   fsm[State.STATE_1.ordinal()][Event.EVENT_2.ordinal()] = State.STATE_2.ordinal();
    //   fsm[State.STATE_1.ordinal()][Event.EVENT_0.ordinal()] = State.STATE_0.ordinal();
    //   fsm[State.STATE_2.ordinal()][Event.EVENT_2.ordinal()] = State.STATE_2.ordinal();
    //   fsm[State.STATE_2.ordinal()][Event.EVENT_1.ordinal()] = State.STATE_1.ordinal();
    //   fsm[State.STATE_2.ordinal()][Event.EVENT_0.ordinal()] = State.STATE_0.ordinal();
    // }

    public final void onEvent(Event event) {
      final State next = State.values()[ fsm[state.ordinal()][event.ordinal()] ];
      if (next ==  State.ERROR) throw new RuntimeException("invalid state transition");
      if (acceptEvent(event)) {
        final State prev = state;
        state = next;
        handleEvent(prev, event);
      }
    }

    public abstract boolean acceptEvent(Event event);
    public abstract void handleEvent(State prev, Event event);
}

如果用大小為S * E的單定義數組替換fsm,它還將改善狀態機的高速緩存接近特性。

我的建議:
您是否可以配置“轉換管理”(即 - 通過XML)。

將XML加載到包含狀態的存儲庫。
內部數據結構將是Map:

Map<String,Map<String,Pair<String,StateChangeHandler>>> transitions;

我選擇的原因是這將是來自州名的地圖
到“輸入”和新狀態的地圖:
每個映射都定義了一個可能的輸入和它所導致的新狀態之間的映射,該狀態由狀態名稱和一個StateChangeHandler定義,我將在后面詳細說明
存儲庫中的更改狀態方法將具有以下簽名:

void changeState(StateOwner owner, String input)

這樣,在使用它的狀態所有者的意義上,存儲庫是無狀態的,你可以復制一個副本,
而不用擔心線程安全問題。
StateOwner將是您需要進行狀態更改的類應該實現的接口。
我認為界面應如下所示:

public interace StateOwner {
   String getState();
   void String setState(String newState);
}

此外,您將擁有ChangeStateHandler接口:

public interface StateChangeHandler {
    void onChangeState(StateOwner, String newState) {
    }
}

當調用存儲庫的changeState方法時,它會
在數據結構中檢查stateOwner的當前狀態是否具有“輸入”映射。 如果它有這樣的映射,它將檢查輸入是否有要更改的新State,並調用onChangeState方法。
我建議你有一個StateChangeHandler的默認實現,當然還有子類,它們將更明確地定義狀態更改行為。

正如我之前提到的,所有這些都可以從XML配置加載,並且使用反射,您可以根據其名稱(如XML中所述)立即動態StateChangeHandler對象,並將保存在存儲庫中。


使用以下幾點依靠並獲得效率和良好性能:
一種。 存儲庫本身是無狀態的 - 不應保留StateOwner的內部引用。
在系統啟動時加載XML一次,之后您應該在內存數據結構中使用。
C。 您將僅在需要時提供特定的StateChangeHandler實現,默認實現應該基本沒有。
d。 無需實例化處理程序的新對象(因為它們應該是無狀態的)

這個提議不是通用的,它不符合UML,簡單來說,它是一個簡單的意思

import java.util.HashMap;
import java.util.Map;

class Mobile1
{
   enum State {
      FIRST, SECOND, THIRD
   }

   enum Event {
      FIRST, SECOND, THIRD
   }

   public Mobile1() {       // initialization may be done by loading a file
      Map< Event, State > tr;
      tr = new HashMap<>();
      tr.put( Event.FIRST, State.SECOND );
      _fsm.put( State.FIRST, tr );
      tr = new HashMap<>();
      tr.put( Event.SECOND, State.THIRD );
      _fsm.put( State.SECOND, tr );
      tr = new HashMap<>();
      tr.put( Event.THIRD, State.FIRST );
      _fsm.put( State.THIRD, tr );
   }

   public void activity() {        // May be a long process, generating events,
      System.err.println( _state );// to opposite to "action()" see below
   }

   public void handleEvent( Event event ) {
      Map< Event, State > trs = _fsm.get( _state );
      if( trs != null ) {
         State futur = trs.get( event );
         if( futur != null ) {
            _state = futur;
           // here we may call "action()" a small piece of code executed
           // once per transition
         }
      }
   }

   private final Map<
      State, Map<
         Event, State >> _fsm   = new HashMap<>();
   private /* */ State   _state = State.FIRST;
}

public class FSM_Test {
   public static void main( String[] args ) {
      Mobile1 m1 = new Mobile1();
      m1.activity();
      m1.handleEvent( Mobile1.Event.FIRST );
      m1.activity();
      m1.handleEvent( Mobile1.Event.SECOND );
      m1.activity();
      m1.handleEvent( Mobile1.Event.FIRST );   // Event not handled
      m1.activity();
      m1.handleEvent( Mobile1.Event.THIRD );
      m1.activity();
   }
}

輸出:

FIRST
SECOND
THIRD
THIRD
FIRST

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM