JavaScript-狀態模式

狀態模式

一個對象有狀態變化
每次狀態變化都會觸發一個邏輯
不能老是用 if...else 來控制javascript

示例:交通訊號燈的不一樣顏色變化css

傳統的 UML 類圖

javascript 中的 UML 類圖

class State {
  constructor(color) {
    this.color = color;
  }
  handle(context) {
    console.log(`turn to ${this.color} light`);
    context.setState(this);
  }
}

class Context {
  constructor() {
    this.state = null;
  }
  setState(state) {
    this.state = state;
  }
  getState() {
    return this.state;
  }
}

// 測試代碼
let context = new Context();

let greed = new State("greed");
let yellow = new State("yellow");
let red = new State("red");

// 綠燈亮了
greed.handle(context);
console.log(context.getState());
// 黃燈亮了
yellow.handle(context);
console.log(context.getState());
// 紅燈亮了
red.handle(context);
console.log(context.getState());

應用場景

有限狀態機

  • 有限個狀態,以及在這些狀態之間的變化
  • 交通訊號燈
  • 利用開源的 lib:JavaScript-state-machine
  • javascript-state-machine
  • 運行 npm install javascript-state-machine --save

有限狀態機的收藏與取消html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Document</title>
  </head>
  <body>
    <p>有限狀態機</p>
    <button id="btn"></button>

    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script src="./03-javascript-state-machine.js"></script>
    <script>
      // 狀態機模型
      var fsm = new StateMachine({
        init: "收藏", // 初始狀態,待收藏
        transitions: [
          {
            name: "doStore",
            from: "收藏",
            to: "取消收藏"
          },
          {
            name: "deleteStore",
            from: "取消收藏",
            to: "收藏"
          }
        ],
        methods: {
          // 執行收藏
          onDoStore: function() {
            alert("收藏成功");
            updateText();
          },
          // 取消收藏
          onDeleteStore: function() {
            alert("已取消收藏");
            updateText();
          }
        }
      });

      var $btn = $("#btn");

      // 點擊事件
      $btn.click(function() {
        if (fsm.is("收藏")) {
          fsm.doStore(1);
        } else {
          fsm.deleteStore();
        }
      });

      // 更新文案
      function updateText() {
        $btn.text(fsm.state);
      }

      // 初始化文案
      updateText();
    </script>
  </body>
</html>

寫一個簡單的 promise

  • promise 就是一個有限自動機
  • promise 三種狀態:pending fullfilled rejected
  • pending -> fullfilled 或者 pending -> rejected
  • 不能逆向變化
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Document</title>
  </head>
  <body>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script src="./03-javascript-state-machine.js"></script>
    <script>
      // 模型
      var fsm = new StateMachine({
        init: "pending",
        transitions: [
          {
            name: "resolve",
            from: "pending",
            to: "fullfilled"
          },
          {
            name: "reject",
            from: "pending",
            to: "rejected"
          }
        ],
        methods: {
          // 成功
          onResolve: function(state, data) {
            // 參數:state - 當前狀態示例; data - fsm.resolve(xxx) 執行時傳遞過來的參數
            data.successList.forEach(fn => fn());
          },
          // 失敗
          onReject: function(state, data) {
            // 參數:state - 當前狀態示例; data - fsm.reject(xxx) 執行時傳遞過來的參數
            data.failList.forEach(fn => fn());
          }
        }
      });

      // 定義 Promise
      class MyPromise {
        constructor(fn) {
          this.successList = [];
          this.failList = [];

          fn(
            () => {
              // resolve 函數
              fsm.resolve(this);
            },
            () => {
              // reject 函數
              fsm.reject(this);
            }
          );
        }
        then(successFn, failFn) {
          this.successList.push(successFn);
          this.failList.push(failFn);
        }
      }

      // 測試代碼
      function loadImg(src) {
        const promise = new MyPromise(function(resolve, reject) {
          var img = document.createElement("img");
          img.onload = function() {
            resolve(img);
          };
          img.onerror = function() {
            reject();
          };
          img.src = src;
        });
        return promise;
      }
      var src = "https://blog-static.cnblogs.com/files/ygjzs/images.gif";
      var result = loadImg(src);
      console.log(result);

      result.then(
        function(img) {
          console.log("success 1");
        },
        function() {
          console.log("failed 1");
        }
      );
      result.then(
        function(img) {
          console.log("success 2");
        },
        function() {
          console.log("failed 2");
        }
      );
    </script>
  </body>
</html>

設計原則驗證

  • 將狀態對象和主題對象分離,狀態的變化邏輯單獨處理
  • 符合開放封閉原則
相關文章
相關標籤/搜索