web-dev-qa-db-ja.com

状態マシンと状態パターンの実装の違いは何ですか?

state machineが単なるstate patternなのか、それとも実際にこれら2つの間に違いがあるのでしょうか?

私はこれを見つけました 太字のタイトル「状態設計パターンvs状態機械」の記事 しかし、結局のところ、彼は状態パターンstate machineを廃止しますが、state machine状態パターン

26
Christoph

同僚にこの違いを説明する方法は、状態パターンは多くのスタンドアロンのカプセル化された状態の分散化された実装であるのに対し、状態マシンはよりモノリシックであるということです。状態マシンのモノリシックな性質は、単一の状態を別のマシンで再利用することが難しくなり、状態マシンを複数のコンパイル単位に分割することが難しくなることを意味します。一方、このモノリシックな設計により、ステートマシンの最適化が大幅に向上し、多くの実装ですべての遷移情報をテーブル内の1か所に表現できます。これは、ステートマシンのアーキテクチャまたは機能の責任者が、実装されているプログラミング言語に精通していない状況に特に適しています。多くのエンジニアリングおよび数学の専攻は、ステートマシンについて学んだが、プログラミング分野。遷移、アクション、およびガードの表をこれらのタイプの人々に提示することは、ページや状態パターンのページよりもはるかに簡単です。

記事は実際には良い読み物でしたが、筆者にはいくつかの点で同意しません。

  • 「オブジェクト指向プログラミング言語を使用しているときに、ステートマシンを使用する理由はもうありません」これは、実行速度に何らかの要件がある場合は、絶対に当てはまりません。
  • 著者の実装が特に短いか単純である、またはブーストステートチャートデジタルカメラよりもメンテナンスの必要性が少ないという考えは、ユースケースと個人的な好みによって異なりますが、断定的に言うことはできません。 http://www.boost.org/doc/libs/1_55_0/libs/statechart/doc/tutorial.html#IntermediateTopicsADigitalCamera

状態の切り替えには割り当てが必要です。これはスピードを殺すつもりです。これは、1つまたは2つのキャッシュミスを節約するために、バッファー内のすべての状態を隣り合わせに配置することで改善できます。ただし、これには著者の例に大きな変更が必要になります。

また、処理されないイベントは、静的状態マシンのようにインライン化および最適化することができないことに注意してください。これは、状態パターンでは、イベントが動的間接層の背後にあるためです。これは、要件によっては効率を大幅に低下させる可能性もあります。

保守の観点からは、未処理のイベントのロギングは、状態パターンを持つ1つの中央スーパーステートから実行できないことに注意してください。また、新しいイベントタイプ/ハンドラー関数を追加するには、すべての状態に関数を追加する必要があります。私はそのメンテナンスをフレンドリーだとは思わない。

また、すべての州の内部構造を調べるのではなく、表のすべての遷移を確認することも好みます。ステートを追加する方が簡単ですが最小限に過ぎません。たとえば、ブーストステートチャートを使用すると、ステートをその親子ステートのリストに追加するだけで済みます。これが唯一の違いです。

速度が問題ではなく、状態マシンの階層がフラットのままである可​​能性が最も高い場合に、私は状態パターンを使用します。作成者は、最初の実装は通常、状態パターンに比べて状態マシンに比べて簡単であり、一般に、より多くのプログラマーがより多くの状態マシンを使用する必要があることを理解しています。

状態パターンの1つの議論は、「オープンクローズ」状態マシンの実装を可能にし、状態マシンをライブラリで定義してユーザーが拡張できるようにすることです。これは、私が主流の状態マシンで知る限り不可能です。フレームワーク。

17
odinthenerd

ステートマシンは、いくつかの方法で設計および実装できます。 1つの方法は、ギャングオブフォーの本に記載されている状態パターンを使用することです。しかし、状態マシンを実装する他のパターンがあります。

たとえば、本Practical UML statecharts in C/C++、2nd ed。 (組み込みシステム向けのイベント駆動型プログラミング)

また、興味深い この質問 が見つかることもあります。

9
Claudio

状態マシンは、実際の状態パターンであるか、実際にこれら2つの間に違いがある

TL; DR:状態を動作が異なる状態に置き換える必要があると想像してください。次に、新しい状態を追加する必要があると想像してください。

完全な回答大きな違いがあります。

状態パターンは状態を抽象化し、それらを互いに分離します。したがって、たとえば、ある特定の状態を別の状態に簡単に置き換えることができます。それでも、新しい状態または新しい遷移、あるいはその両方を追加するときがきたときに、すべての状態を書き直すことに満足することはできません。

状態マシンは、状態図自体を抽象化し、遷移ペイロードから分離します。特定の状態を変更するには、ダイアグラム全体を修正する必要があります。ただし、状態または遷移を追加するには、図を修正するだけです。

2

まだ興味がある人のために、これが私の見解です:

ステートマシンでは、オブジェクトはさまざまな状態になる可能性がありますが、それらの状態ではそれらの動作は実際には気にしません。実際、オブジェクトが次の状態に遷移するときに適用されるアクションのみが考慮されます。 Javaで状態マシンを実装する場合、状態は単なる列挙型または文字列であり、doAction()メソッドを備えたTransitionクラスがあります。

一方、状態パターンでは、遷移については特に気にせず、オブジェクトがそれらの状態でどのように動作するかを考慮します。遷移は、状態の動作を互いに分離するための実装の詳細にすぎません。各状態は、独自のdoAction()メソッドを持つ別個のクラスになります。

ステートパターンがステートマシンを陳腐化させるのは誤りです。状態パターンは、各状態の動作が重要である場合、たとえばオブジェクトが「アイドル」、「攻撃」、「実行」などの状態を持つことができ、各状態でオブジェクトの動作を実装したい場合に役立ちます。 。

ただし、注文オブジェクトの動作を気にしない、オンライン製品の注文のようなユースケースでは。注文が「added_to_cart」状態であるかどうかのみを気にし、「payment_finished」イベントが発行された後、それを「処理中」状態に変更します。この場合、状態はOrderクラスの単純な列挙型プロパティなので、状態マシンを使用する方がはるかに優れています。

1
Bùi Anh Dũng

状態パターンとの違いに気づきました。 UIに使用する場合は、より便利です。状態をロックしたいとしましょう。状態パターンコンテキストでは、ブール値を作成して、状態がそれ以上変化しないようにすることができます。

ここにKotlinの例があります:

     inner class StateContext : State {

       private var stateContext: State? = null
       private var lockState: Boolean = false

       fun isLockState(): Boolean {
           return lockState
       }

       fun setLockState(lockState: Boolean): StateContext {
           this.lockState = lockState//no further actions allowed. useful if you need to permenatley lock out the user from changing state.
           return this
       }

       fun getState(): State? {
           return this.stateContext
       }

       fun setState(state: State): StateContext {
           if (!lockState) this.stateContext = state
           return this
       }

       override fun doAction() {
           this.stateContext?.doAction()
       }
   }

状態マシンでは、それがどのように簡単に行われるかわかりません。

実際の実装の詳細(UIボタンの色の変更など)ではなく、状態(現在の状態の列挙を保存するなど)だけが心配なときは、ステートマシンが本当に好きです。ステートマシンの優れた点の1つは、状態の変化をログに記録する中心的な場所があることです。私は tinderによるkotlinのこのライブラリ を見ました。しかし、個人的には、それらをすべて変更して、やりたいことを実行できると思います。

次にstateMachineとは何ですか? stateMachineは、「次の状態とは何か」についてより詳細に注意します。詳細ではなく、状態の遷移の強調表示です。フローを維持します。通常は、列挙型を使用して作成します。 this article はそれを明確にするのに役立ちます(そして、以下の参照は違いを明確にするのを助けるためにそこから取られます)が、基本的にこれは状態マシンです:

public enum LeaveRequestState {

Submitted {
    @Override
    public LeaveRequestState nextState() {
        return Escalated;
    }

    @Override
    public String responsiblePerson() {
        return "Employee";
    }
},
Escalated {
    @Override
    public LeaveRequestState nextState() {
        return Approved;
    }

    @Override
    public String responsiblePerson() {
        return "Team Leader";
    }
},
Approved {
    @Override
    public LeaveRequestState nextState() {
        return this;
    }

    @Override
    public String responsiblePerson() {
        return "Department Manager";
    }
};

public abstract LeaveRequestState nextState(); 
public abstract String responsiblePerson();

}

これで、各イベントの次の遷移状態がわかります。したがって、状態マシンCARESは、状態の実際の実装を介した遷移に大きく影響します。

LeaveRequestState state = LeaveRequestState.Submitted;

state = state.nextState();
assertEquals(LeaveRequestState.Escalated, state);

state = state.nextState();
assertEquals(LeaveRequestState.Approved, state);

state = state.nextState();
assertEquals(LeaveRequestState.Approved, state);
0
j2emanue