46

私はプログラミングの経験で状態パターンのいくつかの実装に遭遇し、いくつかを実行しました。さまざまなシナリオ (主に UI と解析) で使用されているのを見てきました。問題は、それらのすべてが急速な開発のプレッシャーの下で、保守や理解が困難なコードの塊になってしまったことです。これらの 1 つをリファクタリングすることを検討していますが、オンラインで適切なリソースを見つけるのに苦労しています。State Pattern の簡単な例はオンラインにたくさんありますが、さらに詳細なリソースが必要です。

だから私は探しています:

  • 状態パターンを実装する際の一般的な落とし穴の例とその回避方法
  • 正しく行われた状態パターンの実際の例 (一部のオープン ソース プロジェクト/フレームワークのように)
  • 状態パターンの個人的な経験も大歓迎です

お時間をいただきありがとうございます

4

8 に答える 8

27

@Ivan: Hierarchical State Machines (HSM)については、ウェブ上で利用可能なリソースが多数あります。Miro Samek は、この設計パターンについて詳しく書いており、多くの役立つ情報を提供しています。

興味深い記事:

Mealy と Moore が説明したフラットな FSM ステート チャートよりも HSM を使用する大きな利点は、階層によって責任が分離されることです。サブステートは、明示的に処理するように設計された条件のみを処理する必要があります。未処理のイベントは親ステートに渡されます。親ステートがそれを処理するように明示的に設計されていない場合は、次の上位に渡されます。親など。これにより、それぞれが単一の目的を果たす、つまり単一のオブジェクト内に収まる、小さくて管理しやすいステート マシンを作成できます。新しい機能が追加されたり、新しいクラスが追加されたりすると、世界の自分の小さな部分を処理し、未処理のイベントをそれぞれの親に渡すだけで済みます。

正しく実装すると、循環的複雑度が低く、必要に応じて簡単に変更またはアップグレードできる堅牢なプログラムが得られます。

于 2011-03-08T00:32:52.830 に答える
24

おそらく読んだことがあると思いますが、State Design Patternは、構成要素にその状態が含まれるオブジェクトの動作が状態によって変化する場合に役立ちます。これは、一般的な動作や必要なメソッドを定義するState抽象クラス、インターフェイス、または列挙型のアイデアを意味します。

主な側面

状態パターンを扱う際には、列挙と遷移という 2 つの重要な側面を考慮する必要があります。列挙とは、可能な状態のセット (曜日など) を特定すること、またはワークフロー エンジンの開始、終了、中間など、より抽象的な状態の種類 (メタ状態) を特定することを意味します。遷移とは、動きをモデル化する方法を決定することを意味します。これは通常、表形式の表現 (つまり、Finite State Machine ) ですべての可能な遷移をキャプチャするか、各状態に他の状態への可能な「遷移」を知らせることによって行われます。

通常、トランジションはメタ ステートと密接に関連しています。これは、実行時に新しいステート、つまりトランジションを追加できるような動的システムでは、すべてのステートと関係を事前に知ることができないためです。さらに、遷移アプローチでは、状態自体ではなく、特定の動作 (通知など) が遷移の一部になります。

私が取り組んだ、またはこれが使用施設である場所について議論したいくつかのシナリオがあります。

  1. ワークフロー
  2. コンピューターゲーム対戦相手AI
  3. プロセスのオーケストレーション

ワークフローとは、 jBPMのようなものを意味します。このようなシステムは、適切な人に適切なタイミングで適切な注意を向けさせることに関心があります。彼らは通常、大量の電子メールまたはその他の種類の通知を送信します。また、それらが表すプロセスは、組織の変化に応じて変更できる必要がありますが、管理対象のデータは通常、よりゆっくりと変化します。

コンピューター ゲームの対戦相手 AIは一目瞭然です。私が書いたものではありませんが、持っている人たちとの会話では、これらのシステムは通常自己完結型です。つまり、ワークフローとは異なり、ゲームには通常、コンピューターの対戦相手を制御するために使用されるプロセスを変更する機能がありません。

プロセス オーケストレーションはワークフローに似ていますが、人とのやり取りではなく、システム統合に重点を置いています。Apache Muleフレームワークは一例です。ここで状態は、ステータス (開始、進行中、終了など) とタイプ (ftp 統合ポイント、SQL 統合ポイントなど) を表すことができます。

結論

他の回答とは異なり、状態のカプセル化はソフトウェア システムの変更を管理する優れた方法だと思います。うまくいけば、これらの変更が容易になり、ユーザーが実行時に変更できるようになります。実装の複雑さが増すのと引き換えに、柔軟性を高めるというトレードオフを行います。そのため、このようなアプローチは、たとえばショッピング カートではおそらく役に立たないでしょう。たとえば、動作が非常によく知られており、変更したくない場合です。一方、プロセスが変更される可能性がある場合は、非常にうまく適合します。

于 2011-02-17T03:39:13.077 に答える
8

ほんの 2 セントですが、コード化していない人には理解するのが難しいため、状態パターンは常に維持するのが難しくなります。私は通常、以前の C の経験と同じように、関数/メソッド ポインターの古い標準配列にフォールバックします。行/列の状態/信号を使用して、関数ポインターの 2 次元配列を作成するだけです。理解しやすい。それを管理するクラスがあり、複雑さを処理するために他のクラスに委任します...

my2c

于 2011-02-08T16:54:34.587 に答える
6

ほとんどの場合、状態パターン設計の状態は複数の状態 (または状態のサブステート) を処理するため、維持が難しくなります。

状態に何らかの種類の選択がある場合、ほとんどの場合、複数の状態を処理します。

私は州をきれいに保つために多くの規律を持っています。

これに対する可能な解決策は、より複雑な状態をステートマシン自体 (HSM) にすることです。これにより、処理する状態が少なくなるため、上位レベルでの読みやすさが大幅に向上します。

于 2011-02-10T11:02:00.987 に答える
4

有限状態マシンを見てください。ほとんどすべての成熟した言語には、独自の良い例があります。ご希望の言語を指定していないので、C++ の例を示します: Boost FSM library。ほとんどの場合、必要以上に複雑ですが、デザインのヒントが得られることは間違いありません。

于 2011-02-11T12:53:38.740 に答える
3

だから私は探しています:

  • 状態パターンを実装する際の一般的な落とし穴の例とその回避方法

状態パターンが適切にスケーリングされません。10 個の状態と 10 個の異なる遷移タイプを持つステート マシンを想像してみてください。新しい状態を追加するということは、状態が 10 個の遷移すべてを定義する必要があることを意味します。新しいトランジションを追加するということは、10 個の状態すべてでそれを定義する必要があるということです。つまり、ステート マシンが安定していない場合、および/または多くのステート/トランジションがある場合は、ステート パターンを使用しないでください。

  • 正しく行われた状態パターンの実際の例 (一部のオープン ソース プロジェクト/フレームワークのように)

正しく定義してください:-) https://stackoverflow.com/a/2707195/1168342で引用されている Java の例は JSF ライフサイクル用ですが、遷移は 1 つしかないと思います。他の回答はどれも州について何も引用していません。

  • 状態パターンの個人的な経験も大歓迎です

Head First Design Patterns では、ガムボール マシンの例を使用して状態を説明しています。皮肉なことですが、設計を拡張する (新しい状態または遷移を追加する) たびに、多くのコードが繰り返されます (特に、特定の状態内の無効な遷移について)。また、誰が次の状態を決定するかに応じて、個々の状態クラスを相互に結合できます (状態間の依存関係)。この回答の最後にある説明を参照してください: https://stackoverflow.com/a/30424503/1168342

GoF book は、テーブルベースの代替手段には利点、つまり規則性があると述べています。移行基準を変更するには、(コードではなく) テーブルを変更する必要があります。

于 2015-11-12T16:53:24.443 に答える
1

状態ごとに異なる動作がある場合は、状態パターンを使用する必要があります。実行時にトランジションを再設定する必要があるかもしれません。これを使用するもう 1 つの理由は、後で状態を追加する必要がある場合があることです。

チャイニーズ チェッカーのようなボードゲームで、ポーンの選択、ターゲット スロットの選択などにさまざまな GUI 状態があると想像してください。各状態で、GUI は異なる動作をする必要があります。一部の入力は処理する必要があり、その他は無視されます。単純なスイッチ/ケースを使用することは可能ですが、ロジックがカプセル化され、関連するコードがまとめられているため、状態パターンが便利です。これにより、他の状態のほとんど/すべてに影響を与えることなく、新しい状態を簡単に導入できます (遷移を設定する責任者に応じて: 状態が発信遷移を知っているか、コンストラクターなどを使用して実行時にそれらを与えることができます)。

この例でわかるように、GuiController は IGuiState インターフェイスを使用して、オンデマンドで動作を変更します。実装はここで見ることができます

主な落とし穴は、柔軟にする必要がある場合にスイッチ/ケースを使用することです。間接化にはもう少し時間がかかるため、かなり単純な状態の一定量の場合は、それをお勧めします。かなり高速な低レベルのネットワーク プロトコルを実装する必要がありますが、これは通常、オーバーヘッドが大きくなります。

于 2011-02-16T22:42:01.413 に答える
0

要素のセットを評価する機能を持つ式評価器を構築しています。状態パターンは、状態に応じてセットに対して実行できることと実行できないことを区別するのに非常に役立つことがわかりました。つまり、開いている、閉じている、非アクティブ、アクティブなどです。FSM は非常に簡単に描画でき、囲まれた属性に応じて機能が何をすべきかを定義する ifelse ステートメントの巨大なブロックを不要にすることで、コードの複雑さを軽減します。条件をクラスにすることで、これらの条件をより明確にします。今までで一番好きなパターンです。

于 2015-10-24T19:33:23.783 に答える