2

これが他の場所で答えられた場合はお詫びします。これを行うための最良の方法を自分に納得させるのに十分な情報を見つけることができませんでした。また、これはコードのない長い説明であることも理解していますが、私が行っていることを示すためにサンプルコードを作成する必要があるかどうかを教えてください。

基本的に:

  • System.in/outを使用してJavaで通信プロトコルを実装する
  • 現在のアプローチは、スキャナーがコンテキストクラスのSystem.inでインスタンス化される状態パターンを実装しています。
  • 具体的な状態は、コンテキストメソッドを呼び出してスキャナーから読み取り、その後、スキャナーから返された値に基づいてアクション/遷移状態を適切に実行します。

状態パターンを使用する私の当初の意図は、System.inからこのようなシーケンスを解析するときにコードを単純化することでした(構文については聞かないでください。これは私が使用する必要があるものです)。

  • コマンド名=X
  • ヘッダ
  • ヘッダー情報の行
  • コンテンツ
  • コマンドの内容の行
  • ENDCONTENTS
  • エンドコマンド

私は通常、受け取ると予想されるコマンドのタイプごとに具体的な状態を定義します。上記のシーケンスを例として使用すると、{WAITING_FOR_COMMAND、COMMAND_RECEIVED、PARSING_HEADER、PARSING_CONTENTS、PARSING_DONE、COMMAND_PROCESSED}のような状態セットが作成されます。最初はWAITING_FOR_COMMANDにいて、「COMMAND NAME = X」を受信するとCOMMAND_RECEIVEDに移行し、「HEADER」が入ったらPARSING_HEADERなどに移行します。この設計では、すべてのエッジケースをトラバースします。プロトコルが簡単になり、プロトコルが微調整されたときのコードの更新/保守も簡単になります。明らかに、大規模なswitchステートメントや反復的な境界チェックよりもはるかに優れています。

私が抱えている問題は、具体的な状態の動作を具体化するにつれて、コンテキストクラスでますます多くの状態変数を宣言していることに気付くということです。これは、非常に公開されたインターフェイスと、コンテキストと具体的な状態クラス。このプロトコルのコマンドシーケンスは任意の長さにすることができ、コマンドシーケンスが完了するまで、コマンドシーケンスの各項目によって与えられた情報を保存する必要があります。

上記のコマンドシーケンスを例にとると、「COMMAND ID = X」の後に、「ENDCOMMAND」を受け取ってコマンドを完全に処理した後、後で使用できるように値Xを保存します。「HEADER」の後に、実際にコマンドを処理するときに「ENDCOMMAND」を受け取った後、将来使用するためにヘッダー情報を保存したいと思います。などなど。コンテキストクラスにcommandIdとヘッダー状態変数を追加するだけで今のところは機能しますが、私にはまったくきれいに見えないか、うまくカプセル化されていないようです。

誰かがこの問題にどのように取り組むかについての高レベルの提案がありますか?このための状態デザインパターンのより良い使用法はありますか?

私が遊んでいるいくつかのアイデアに注意してください:

  • 各タイプのコマンドシーケンスの状態コンテキストを定義し、System.inから関連するコマンドを受け取ったときに適切なコンテキストを呼び出します。これは巨大なスイッチブロックを持っているのとほぼ同じくらい厄介なようで、デザインを過度に複雑にしているようです
  • 複合FSMをサポートする本格的なFSMアーキテクチャを設計します。このアーキテクチャでは、各コマンドシーケンスが包括的なFSM内で独自のFSMを占有します。これは私にはやり過ぎのようです
  • コマンドシーケンスタイプごとにさまざまなサブクラスを持つProtocolCommandオブジェクトを作成します。移行時にこれらを各状態に渡し、進行するにつれて徐々に構築することができます...しかし、これにより状態インターフェイスが乱雑になり、すべての状態に、必ずしも使用しないパラメータを取り込むように強制されます

本当にありがとう!申し訳ありませんが、これは非常に冗長でした。何か明確にできるかどうか教えてください。

4

2 に答える 2

1

このスタイルの解析には列挙型を使用し、状態ごとに1つの列挙型を使用しました。例はここにあります http://vanillajava.blogspot.co.uk/2011/06/java-secret-using-enum-as-state-machine.html

interface Context {
    ByteBuffer buffer();
    State state();
    void state(State state);
}
interface State {
    /**
       * @return true to keep processing, false to read more data.
     */
    boolean process(Context context);
}
enum States implements State {
    XML {
        public boolean process(Context context) {
            if (context.buffer().remaining() < 16) return false;
            // read header
            if(headerComplete)
                context.state(States.ROOT);
            return true;
        }
    }, ROOT {
        public boolean process(Context context) {
            if (context.buffer().remaining() < 8) return false;
            // read root tag
            if(rootComplete)
                context.state(States.IN_ROOT);
            return true;
        }
    }
}

public void process(Context context) {
    socket.read(context.buffer());
    while(context.state().process(context));
}
于 2012-07-03T19:16:58.697 に答える
0

私は最初に次の点を考慮します:

  1. 状態コンポーネント(WAITING_FOR_COMMAND、COMMAND_RECEIVEDなど)はアプリケーションロジックの中心ですか?つまり、アプリが正しく機能するためには、パーサーがどのような状態にあり、どのような状態に移行するかが非常に重要ですか?各状態が保持しようとしている値は何ですか?これらの値は州ごとに大きく異なりますか?あなたの答えが「はい」の場合、おそらくあなたはステートパターンの有効なケースを持っていますが、それが単にやり過ぎだった場所でステートマシンが使用されているのをよく見ました。

  2. あなたのユースケースは、コマンドパターンとインタプリタパターンを必要としているように見えます(文法を書いているようです)。

于 2012-07-03T20:06:05.003 に答える