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)

このように、状態所有者がそれを使用するという意味で、リポジトリはステートレスであり、1 つのコピーをコピーでき、
スレッド セーフの問題を心配する必要はありません。
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 の内部参照は保持されません。
b. システムの起動時に XML を 1 回ロードすると、その後はインメモリ データ構造で作業する必要があります。
c. 必要な場合にのみ特定の StateChangeHandler 実装を提供します。デフォルトの実装は基本的に何もしません。
d. ハンドラーの新しいオブジェクトをインスタンス化する必要はありません (ステートレスである必要があるため)

于 2012-11-02T13:35:30.177 に答える
0

この提案は普遍的ではなく、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
于 2012-11-01T13:01:32.370 に答える