2012-11-01 85 views
1

我正在寫一個java模擬應用程序,它有很多要模擬的實體。這些實體中的每一個在系統中隨時都有一定的狀態。對這種實體建模的一種可能的自然方法是使用state (or state machine)模式。問題是,如果有很多狀態切換,它會在運行時創建大量對象,這可能會導致系統性能不佳。我有什麼設計方案?我希望性能成爲可維護性之後的主要標準。java中高效的狀態機模式

感謝

+3

有多確定對象創建將成爲模擬的瓶頸? – aioobe

+0

我在想enum + switch –

+4

「might」是你問題中的關鍵字:你創建了更多的對象,但你不希望這樣的表現是不可接受的!在分析之前優化是一個壞主意;編碼您的理想解決方案,然後優化。 – dasblinkenlight

回答

1

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

將XML加載到存儲狀態的存儲庫。
內部數據結構將是一個地圖:

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) { 
    } 
} 

當倉庫的改變狀態的方法被調用時,它會
檢查在數據結構上,stateOwner的當前狀態具有「輸入」的映射。 如果它有這樣一個映射,它將檢查輸入是否有一個新的狀態改變,並調用onChangeState方法。
我會建議你有一個StateChangeHandler的默認實現,當然還有子類,它們會更明確地定義狀態變化行爲。

正如我前面提到的,所有這些都可以從XML配置中加載,並且使用反射可以基於它們的名稱(如XML中提到的)立即對StateChangeHandler對象進行實時處理,並將保存在存儲庫中。


效率和良好的性能依賴和獲得使用以下幾點:
a。存儲庫本身是無狀態的 - 不應該保留StateOwner的內部引用。
b。您在系統啓動時加載一次XML,然後在內存數據結構中進行處理。
c。您只會在需要時提供特定的StateChangeHandler實現,默認實現應該基本不做任何事情。
d。沒有必要實例化處理程序的新對象(因爲它們應該是無狀態的)

0

這個建議是不是普遍的,它不是UML compliantsimple thing, it's a simple mean

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 
+0

這當然很簡單,但對於使用狀態機的情況來說,這太過分了。有可能做得更好。 – duffymo

+0

我正在等待您的提議 – Aubin

+0

請參閱上文。由XML驅動的類。發佈的代碼過多。 「符合UML」是什麼意思? UML與任何事情無關,更不用說這個問題了。 – duffymo

3

下面的代碼將會給您提供高性能的(10ns的〜/事件)零運行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); 
} 

如果fsm被大小爲S * E的單目數組替代,它也將改善狀態機的緩存接近度特性。

+0

本地數組分配新(零GC?),並查找你必須編寫自己的「for」循環。性能?事件和國家不能是通用的,它們是具體的每個具體類。 – Aubin

+0

@Aubin - 運行時零GC。我已經演示了保證高性能和可擴展性的技術,SxN,類可以儘可能地泛化,轉換表可以帶到子類,等等。 – bobah