状態機械を使用して論理を解く


この記事は次のように役に立つでしょう.
  • あなたはJS/オブジェクト指向言語を読むことができます(Python、C++、C - Chether、Javaなど)
  • 書き込み機能に精通しています.https://stackoverflow.com/a/4709224 )

  • 導入
    数週間前、私はボタンの視認性をコントロールしなければならなかったアプリケーションに取り組んでいました.私が出発した瞬間から、私は次のことを知っていた.
  • 私のボタンは目に見えるか見えない.
  • 私のボタンが見えない間、どんなキーも押されるならば、それは見えるようになります.
  • 私のボタンが表示されると、3秒タイマーが起動します.
  • タイマーが期限切れになった場合、ボタンは見えなくなる.
  • 私のボタンが表示されている間にキーが押された場合、タイマーは再起動します.
  • このロジックを次の図で説明できます.

    私はそれが十分に良いと決めたので、私はすぐにコーディングを始めた.私のコードは次のように見えました.
    // Code simplified for explanation purposes
    function onKeyPress() {
      if(button.visible) {
        restartTimer();
      } else {
        button.visible = true;
      }
    }
    
    function restartTimer() {
      if(timer.exists) {
        timer.delete();
      }
    
      timer = new Timer("3 seconds");
      if(timer.elapsed) {
        button.visible = false;
      }
    }
    
    私は本当に結果を感じていなかった.私の美しいボタンは、トランジションやアニメーションの多くなしで画面の外に飛び込んでいた.私のチームのデザイナーは私の仕事に満足していないことを知っていたので、私は仕事にいくつかの空想を加えることにしました.私は1 s不透明度の移行を選択し、コーディングに戻った.こんな感じで終わりました.
    // Code simplified for explanation purposes
    function onKeyPress() {
      if(button.visible) {
        restartTimer();
      } else {
        // Wait for transition to complete.
        waitTransition("1 second", "opacity=1")
        button.visible = true;
      }
    }
    
    function restartTimer() {
      if(timer.exists) {
        timer.delete();
      }
    
      timer = new Timer("3 seconds");
      if(timer.elapsed) {
        waitTransition("1 second", "opacity=0")
        button.visible = false;
      }
    }
    
    しかし、これは私のコードに新しいバグを導入しました.あなたはそれを見つけることができますか?コードに戻ると、それを見つけることができるかどうかを確認してください.
    あなたはそれを見つけましたか?心配しないでください!それを見つけるのに少し時間がかかった.ここで手掛かり:あなたがキーを押した場合、何が起こるかは、移行が起こっている間?キーを押したので、タイマーを再起動し、ボタンの不透明度を1に戻る必要があります.
    どこでこれを加えるべきですか.新しいものを加えることにしたisFadingOut 私のボタンに対するプロパティーです.
    // Code simplified for explanation purposes
    function onKeyPress() {
      if(button.isFadingOut) {
        waitTransition("1 second", "opacity=1");
        button.visible = true;
      }
      else if(button.visible) {
        restartTimer();
      } else {
        // Wait for transition to complete.
        waitTransition("1 second", "opacity=1")
        button.visible = true;
      }
    }
    
    function restartTimer() {
      if(timer.exists) {
        timer.delete();
      }
    
      timer = new Timer("3 seconds");
      if(timer.elapsed) {
        // Wait for transition to complete.
        button.isFadingOut = true;
        waitTransition("1 second", "opacity=0")
        button.isFadingOut = false;
        button.visible = false;
      }
    }
    
    これはバグの新しいリストを作成することになったrace condition . これは手に負えなかった!現在、私は同時にいくつかのタイマーに対処しなければなりませんでした.私が新しいものを加えなければならないかfadingIn 状態?どれくらい、私のコードを台無しにしますか?私は、私が問題に接近した方法を変える時間であると決めました.

    ステートマシンは日を保存します.
    不透明度遷移が図に新しい要素を作成したことに気づいたかもしれません.

    このダイアグラムはステートマシンを表します.それはどのように描くことができる方法の一つです.ステートマシンは、すべての状態と我々のアプリケーションの移行を視覚化する素晴らしいツールです.すべての円は状態を表し、すべての矢印は状態間の遷移です.また、私たちは、州間の移行に必要なすべての異なる入力を見るのを助けます.すべてのすべてで、彼らはブールの混乱のほぼすべての並べ替えを解くための素晴らしい方法です

    これはすべて素晴らしいですが、どのように私はそれらを使用しますか?
    ステートマシンを実装できる方法の一つはenumerators . JavaScriptはネイティブに存在しませんが、オブジェクトを使用してシミュレートすることができます.
    const buttonStates = {
      // You could also map these to a number instead of the same string,
      // but this is personal preference as it's easier to debug.
      fadingOut: "fadingOut",
      visible: "visible",
      invisible: "invisible"
    };
    
    次に、現在のボタンの状態をプロパティーに保存できます.
    // start with a default state
    button.state = buttonStates.visible;
    
    州間の移行を担当する新しい機能を追加する必要があります.
    function changeState(newState) {
      button.state = newState;
    
      if(newState === buttonStates.visible) {
        clearTransitions();
        waitTransition("1 second", "alpha=1");
        restartTimer();
      }
    
      if(newState === buttonStates.fadingOut) {
        waitTransition("1 second", "alpha=0")
      }
    }
    
    最後に、我々の新しい状態を考慮に入れるために以前の関数の両方を考慮する必要があります.
    function onKeyPress(){
      if(button.state === buttonStates.visible) {
        restartTimer();
      }
    
      if(button.state === buttonStates.invisible) {
        changeState(buttonStates.visible) 
      }
    
      if(button.state === buttonStates.fadingOut) {
        changeState(buttonStates.visible)
      } 
    }
    
    function restartTimer() {
      if(timer.exists) {
        timer.delete();
      }
    
      timer = new Timer("3 seconds");
      if(timer.elapsed) {
        changeState(buttonStates.fadingOut)
      }
    }
    
    これはデバッグを容易にするだけではなく、我々のボタンに新しい状態を追加することをより簡単にします.例として、新しいfadingIn 状態:
  • 列挙子への追加
  • 新しいif文の両方を追加するchangeState and restartTimer .
  • 一旦これが行われるならば、あなたはこのロジックが我々が以前にしたものと簡単に衝突しないのに気づくかもしれません.すべての州は、それ自身のブロックに分割される異なったふるまいを持ちます.

    いつ使用するのですか.
    私が言及したように、州のマシンはいくつかのユースケースに最適なツールです.彼らは日々のツールで実装され、現代図書館などで見られるxstate . しかし、彼らは常に使用する必要はありません.状態機械が我々の論理をより複雑にするかもしれないいくつかのケースがあります.ここでは、私は彼らと一緒に作業中に発見した長所と短所のリストです
    長所
  • それぞれの状態を自分のブロックに分離することで、デバッグを容易にする
  • それはあなたのアプリケーションに新しい状態を追加するのは簡単です
  • 彼らはあなたのコードを読みやすくする.
  • 短所
  • 彼らは学習曲線を持っています、彼らに慣れていない人々は彼らを混乱させるかもしれません.
  • ではなく、あなたが取り組んでいるON - OFFボタンを実装する最良の方法.

  • ステートマシンについて
    ENUMとif/elseステートメントを使用することは、ステートマシンを作成する唯一の方法ではありません.これはあなたがそれを行うために取ることができるアプローチの一つです.ここでは、それらについての詳細を学ぶことができる場所のリストです
  • Finite-state machines on Wikipedia
  • XState's docs
  • State machines in game development
  • This great explanation I found on Stackoverflow while writing this article
  • おい!私の記事を読んでくれてありがとう.あなたが新しい何かを学んだか、私の毎日のdev闘争を楽しむならば、さえずりの上で私について来てください.
    すぐにね!