0

(英国) Traffic Light シミュレーション アプリがあり、TrafficLightクラスには次のように定義された関連する有限状態マシンがあるとします。

*  -->  RED  -->  RED_AMBER  -->  GREEN  -->  AMBER  -->  RED  --> ...
(repeat until the proverbial cows make an appearance )

建設中のTrafficLightの状態は RED です ある種のタイム トリガーが状態の変化を引き起こします。

アプリには(ポイントから離れたコードを削除する)のようなコードがあるかもしれません...

TrafficLight trafficLightsAtBigJunction = new TrafficLight();  // state = RED
trafficLightsAtBigJunction.setState( TrafficLightState.RED_AMBER );
trafficLightsAtBigJunction.setState( TrafficLightState.GREEN );
trafficLightsAtBigJunction.setState( TrafficLightState.AMBER );
trafficLightsAtBigJunction.setState( TrafficLightState.RED );
trafficLightsAtBigJunction.setState( TrafficLightState.RED_AMBER );
:
:
:

重要なのは、ステート パターンを使用してステート マシンを実装することです。

TrafficLight trafficLightsAtBigJunction = new TrafficLight();  // state = RED
trafficLightsAtBigJunction.setState( TrafficLightState.GREEN ); // Exception!!!!!

これは不正な状態移動であるため、(私たちの設計により) 例外がスローされます。それが私たちが望むものです。すべてが世界とうまくいっています。

しかし、信号機を持続し、それがたまたま state = AMBER である場合、問題があるように見えます。ユーザーが 3 日後に戻ってきて素晴らしい信号機のシミュレーションを見ると、現在の状態から一部の (気にしない) 永続的なストアに復元されます。

ここで状態パターンが提供するカプセル化を壊さずに、信号機インスタンスを AMBER 状態にする方法は?

2 つの選択肢があるようです:- (1) 新しいインスタンスを作成し、関連する状態を実行します (2)慣例により、永続ストアからの読み取り後にのみ使用される、必要な状態に設定する特別なメソッドを提供します. 例えば

trafficLight.setStateAfterReadingFromPersistanceSource( AMBER );

(1)の問題は、状態を実行するときに望ましくない副作用が発生する可能性が非常に高く、さらに状態マシンによってはロジックが非常に複雑になる可能性があることです

(2) の問題は、明らかに慣例に従ってのみ機能するため、誤って使用すると知らずにバグが発生する可能性があることです。さらに重要なことに、最初に望んでいたカプセル化されたすべての素敵なパターンがほとんど壊れてしまいます。

問題は永続化技術にとらわれないことです-ORM、ファイル、シリアライゼーションなどと同じ問題です

ここに解決策があると思いますが、自分では考えられず、グーグルのスキルが十分ではありませんでした。

どんな指針も素晴らしいでしょう。

4

4 に答える 4

2

状態と遷移をオブジェクトとして表現することでステート マシンを実装することは確かに可能ですが、これらのオブジェクトには初期化が必要であり (これが問題のようです)、貴重な RAM を使用します。

ただし、ステート マシンを純粋なコードとして実装するまったく異なる方法もあります。これには非常に多くの利点があるため、「データとしてのステート マシン」方式に戻ることはありません。

具体的な例として、 http: //www.drdobbs.com/architecture-and-design/uml-statecharts-at-1099/188101799にある DDJ の記事「UML Statecharts at $10.99」では、Pedestrian LIGHT CONtrolled ( PELICAN) を階層的なステート マシンとして交差させます。

この例は、ローエンドのマイクロコントローラー用に C でコーディングされています。C++ の実装に興味がある場合は、SourceForge.net ( https://sourceforge.net/projects/qpc/files/QP_C%2B%2B/)から入手できるオープン ソースの QP/C++ フレームワークを参照してください。

于 2013-02-09T15:09:52.317 に答える
1

私の見方では、状態を操作するには2つの方法が必要です。

1) この状態から別の状態への遷移、この遷移のすべての副作用の実行、不正な場合は例外のスローなど

2) マシンを状態/内部値のセットに直接設定します。他に何もしないでください。

FSM の内部状態を記述するすべてを保持し、前者を実行するメソッドと後者を実行するメソッドの 2 つのメソッドを用意する必要があります。

後者は、セットアップ時または永続化解除時に使用されます。また、他に何が起こる必要があるかを気にせずに値を変数に転送するだけなので、コーディングもはるかに簡単です。

前者はシミュレーション中に使用されます。

于 2013-02-06T21:43:12.363 に答える
1

最も簡単な方法は、初期状態をコンストラクターのパラメーターとして渡すことです。システムがすべてのライトを赤で開始するのは、あなたの慣例です。

別のアプローチは、ストアからデータをプルする関数を友人またはメンバーにすることです (それを読み取るために operator>> を使用しているか、他の何かを使用しているかによって異なります)。これにより、例に従って状態に遷移するか、ストアから初期状態を読み取るかを選択できます。何が起こっているのかについてはあまり曖昧ではありません。FSM の状態やその他の必要なものを保持するときにストアとやり取りするのは FSM 次第です。

于 2013-02-06T22:39:48.730 に答える
1

短い答えとして、この単純化された例では、コンストラクター引数として渡すことができるというピートに同意します。

しかし、正直なところ、デザイン全体に欠陥があると思います。これは、標準の State デザイン パターンを使用してモデル化する必要があると思います。このようなもの:

class TrafficLight 
{
   private TrafficLightState _lightState;

   TrafficLight(initialState)
   {
      // utilize lookup table or factory-method to assign _lightState with  the correct TrafficLightState subclass
   }

   // UI can use this to identify/render the appropriate color
   Color getColorCode()
   {
      return _lightState.getColorCode();
   }

   // UI uses this to know when to signal the next light change (each color can have different duration)
   int getDuration()
   {
      return _lightState.getDuration();
   }

   // assuming the UI has a timer that is set based on the current light's duration
   void changeLight()
   {
      TrafficLightState nextState = _lightState.onChangeLight();
      _lightState = nextState;
   }

}

abstract class TrafficLightState
{
   abstract Color getColorCode()
   abstract TrafficLightState onChangeLight()
   abstract int getDuration()
}


class RedLight : TrafficLightState
{

   Color getColorCode()
   {
      return Color.Red;
   }

   TrafficLightState  onChangeLight()
   {
      return new RedAmberLight();
   }

   int getDuration() 
   {
      return 30;
   }
}

class RedAmberLight : TrafficLightState
{

   Color getColorCode()
   {
      return Color.Orange;
   }

   TrafficLightState  onChangeLight()
   {
      return new GreenLight();
   }

   int getDuration() 
   {
      return 10;
   }
}

class GreenLight: TrafficLightState
{

   Color getColorCode()
   {
      return Color.Green;
   }

   TrafficLightState  onChangeLight()
   {
      return new AmberLight();
   }

   int getDuration() 
   {
      return 25;
   }
}

class AmberLight: TrafficLightState
{

   Color getColorCode()
   {
      return Color.Yellow;
   }

   TrafficLightState  onChangeLight()
   {
      return new RedLight();
   }

   int getDuration() 
   {
      return 10;
   }
}

ステート マシンには、通常の操作で遷移するために使用される、明示的に公開された「状態の変更」メソッドを含めることはできません。代わりに、ステート マシンが独自の状態に遷移できるようにする刺激があると考えてください。この例では、刺激は非常に単純ですが、通常、状態遷移を引き起こす可能性のある入力のバンクがあります。しかし、適切にカプセル化すれば、発信者は詳細を過度に意識する必要はありません。

于 2013-02-07T15:35:33.320 に答える