1

我正在编写一个 java 模拟应用程序,它有很多实体要模拟。这些实体中的每一个在系统中的任何时候都具有一定的状态。对这种实体建模的一种可能且自然的方法是使用状态(或状态机)模式。问题是如果有很多状态切换,它会在运行时创建很多对象,这可能会导致系统性能不佳。我有哪些设计替代方案?我希望性能成为可维护性之后的主要标准。

谢谢

4

3 回答 3

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 的单向数组,它还将改善状态机的高速缓存邻近特性。

于 2012-11-01T13:19:46.323 回答
1

我的建议:
您是否可以配置“转换管理”(即通过 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 的当前状态是否具有“输入”映射。如果它有这样的映射,它将检查输入是否有要更改的新状态,并调用 onChangeState 方法。
我会建议你有一个 StateChangeHandler 的默认实现,当然还有更明确​​地定义状态改变行为的子类。

正如我之前提到的,所有这些都可以从 XML 配置中加载,并且使用反射,您可以根据名称(如 XML 中所述)实例化 StateChangeHandler 对象,并将其保存在存储库中。


效率和良好的性能依赖于并使用以下几点获得:
一种。存储库本身是无状态的 - 不应保留 StateOwner 的内部引用。
湾。您在系统启动时加载一次 XML,之后您应该使用内存数据结构。
C。您将仅在需要时提供特定的 StateChangeHandler 实现,默认实现基本上什么都不做。
d。无需实例化处理程序的新对象(因为它们应该是无状态的)

于 2012-11-02T13:35:30.177 回答
0

这个提议不是通用的,它不符合 UML ,但对于简单的事情来说,它是一个简单的 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
于 2012-11-01T13:01:32.370 回答