我正在编写一个java模拟应用程序,它有很多要模拟的实体。这些实体中的每一个在系统中的任何时间都具有某种状态。对此类实体进行建模的可能且自然的方法是使用state (or state machine)模式。问题是如果有很多状态切换,它会在运行时创建很多对象,这可能会导致系统性能下降。我有哪些设计选择?我希望性能成为可维护性之后的主要标准。
由于
答案 0 :(得分:3)
以下代码将为您提供高性能(~10ns / event)零运行时GC状态机实现。只要系统或组件中有状态概念,就可以使用显式状态机,这不仅可以使代码清晰可扩展,还可以让人们(甚至程序员)立即看到系统的功能,而无需深入研究多个回调: / p>
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,它还将改善状态机的高速缓存接近特性。
答案 1 :(得分: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的当前状态是否具有“输入”映射。
如果它有这样的映射,它将检查输入是否有要更改的新State,并调用onChangeState方法。
我建议你有一个StateChangeHandler的默认实现,当然还有子类,它们将更明确地定义状态更改行为。
正如我之前提到的,所有这些都可以从XML配置加载,并且使用反射,您可以根据其名称(如XML中所述)立即动态StateChangeHandler对象,并将保存在存储库中。
使用以下几点依靠并获得效率和良好性能:
一个。存储库本身是无状态的 - 不应保留StateOwner的内部引用。
湾在系统启动时加载XML一次,之后您应该在内存数据结构中使用。
C。您将仅在需要时提供特定的StateChangeHandler实现,默认实现应该基本没有。
d。无需实例化处理程序的新对象(因为它们应该是无状态的)
答案 2 :(得分:0)
此提案不是通用的,不是UML compliant,而是simple 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