JavaScript 設計模式之策略模式

前言

在軟體工程中,設計模式(design pattern)是對軟體設計中廣泛存在(反覆出現)的各類問題,所提出的解決方案。javascript

設計模式並不直接用來完成程式碼的編寫,而是描述在各類不一樣狀況下,要怎麼解決問題的一種方案。java

設計模式能使不穩定轉爲相對穩定、具體轉爲相對抽象,避免會引發麻煩的緊耦合,以加強軟體設計面對並適應變化的能力git

——維基百科github

設計模式是一種軟件開發的思想,有益於下降代碼的耦合性,加強代碼的健壯性。每每在大型項目中用的比較多。算法

今天就來介紹一種能夠替代選擇運算的設計模式——策略模式。設計模式

介紹

策略模式做爲一種軟件設計模式,指對象有某個行爲,可是在不一樣的場景中,該行爲有不一樣的實現算法。好比每一個人都要「交我的所得稅」,可是「在美國交我的所得稅」和「在中國交我的所得稅」就有不一樣的算稅方法。單元測試

策略模式:學習

  • 定義了一族算法(業務規則);
  • 封裝了每一個算法;
  • 這族的算法可互換代替(interchangeable)。

——維基百科測試

能夠看出,爲應對不一樣場景所致使算法不一樣,基於工廠模式將各個算法進行封裝成類,再進行使用,這就是策略模式。ui

案例

咱們來一個例子,通常狀況下,若是咱們要作數據合法性驗證,不少時候都是按照 swith 語句來判斷(也能夠是 if,elseif,else 的結構),可是這就帶來幾個問題:

  • 若是增長需求的話,咱們還要再次修改這段代碼以增長邏輯。
  • 在進行單元測試的時候也會愈來愈複雜。

代碼以下:

validator = {
  validate: function(value, type) {
    switch (type) {
      case "isNonEmpty ": {
        return true;
      }
      case "isNumber ": {
        return true;
        break;
      }
      case "isAlphaNum ": {
        return true;
      }
      default: {
        return true;
      }
    }
  }
};
// 測試
alert(validator.validate("123", "isNonEmpty"));
複製代碼

那如何來避免上述代碼中的問題呢,根據策略模式,咱們能夠將相同的工做代碼單獨封裝成不一樣的類,而後經過統一的策略處理類來處理,OK,咱們先來定義策略處理類,代碼以下:

var validator = {
  // 全部能夠的驗證規則處理類存放的地方,後面會單獨定義
  types: {},
  // 驗證類型所對應的錯誤消息
  messages: [],
  // 固然須要使用的驗證類型
  config: {},
  // 暴露的公開驗證方法
  // 傳入的參數是 key => value對
  validate: function(data) {
    var i, msg, type, checker, result_ok;
    // 清空全部的錯誤信息
    this.messages = [];
    for (i in data) {
      if (data.hasOwnProperty(i)) {
        type = this.config[i]; // 根據key查詢是否有存在的驗證規則
        checker = this.types[type]; // 獲取驗證規則的驗證類
        if (!type) {
          continue; // 若是驗證規則不存在,則不處理
        }
        if (!checker) {
          // 若是驗證規則類不存在,拋出異常
          throw {
            name: "ValidationError",
            message: "No handler to validate type " + type
          };
        }
        result_ok = checker.validate(data[i]); // 使用查到到的單個驗證類進行驗證
        if (!result_ok) {
          msg = "Invalid value for *" + i + "*, " + checker.instructions;
          this.messages.push(msg);
        }
      }
    }
    return this.hasErrors();
  },
  // helper
  hasErrors: function() {
    return this.messages.length !== 0;
  }
};
複製代碼

而後剩下的工做,就是定義 types 裏存放的各類驗證類了,咱們這裏只舉幾個例子:

// 驗證給定的值是否不爲空
validator.types.isNonEmpty = {
  validate: function(value) {
    return value !== "";
  },
  instructions: "傳入的值不能爲空"
};

// 驗證給定的值是不是數字
validator.types.isNumber = {
  validate: function(value) {
    return !isNaN(value);
  },
  instructions: "傳入的值只能是合法的數字,例如:1, 3.14 or 2010"
};

// 驗證給定的值是否只是字母或數字
validator.types.isAlphaNum = {
  validate: function(value) {
    return !/[^a-z0-9]/i.test(value);
  },
  instructions: "傳入的值只能保護字母和數字,不能包含特殊字符"
};
複製代碼

使用的時候,咱們首先要定義須要驗證的數據集合,而後還須要定義每種數據須要驗證的規則類型,代碼以下:

var data = {
  first_name: "Tom",
  last_name: "Xu",
  age: "unknown",
  username: "TomXu"
};

validator.config = {
  first_name: "isNonEmpty",
  age: "isNumber",
  username: "isAlphaNum"
};
複製代碼

最後,獲取驗證結果的代碼就簡單了:

validator.validate(data);

if (validator.hasErrors()) {
  console.log(validator.messages.join("\n"));
}
複製代碼

總結

策略模式定義了一系列算法,從概念上來講,全部的這些算法都是作相同的事情,只是實現不一樣,他能夠以相同的方式調用全部的方法,減小了各類算法類與使用算法類之間的耦合。

從另一個層面上來講,單獨定義算法類,也方便了單元測試,由於能夠經過本身的算法進行單獨測試。

實踐中,不只能夠封裝算法,也能夠用來封裝幾乎任何類型的規則,是要在分析過程當中須要在不一樣時間應用不一樣的業務規則,就能夠考慮是要策略模式來處理各類變化。

-EFO-


筆者專門在 github 上建立了一個倉庫,用於記錄平時學習全棧開發中的技巧、難點、易錯點,歡迎你們點擊下方連接瀏覽。若是以爲還不錯,就請給個小星星吧!👍


2019/04/24

AJie

相關文章
相關標籤/搜索