JavaScript設計模式之策略模式【組合委託】

bigname22發表於2020-08-18

前言:語言只是工具,思想才是核心

今天要總結的是 策略模式
策略在開發中的應用非常廣泛,所以也是非常常見且使用的設計模式。
在實際開發中,往往在實現一個功能時,有多種解決方案可行。

常見場景:

  • 解壓:gzip演算法解壓,gzip演算法解壓
  • 表單驗證:手機號驗證,郵箱驗證,密碼格式驗證
  • 工資計算:技術猛男工資,鼓勵師妹妹工資,冷血產品經理工資

總結策略模式的使用場景,在想實現某個效果時,有多種不同的達成方式。這個時候就要考慮策略模式(如果你寫了很多ifelse)

使用策略模式的優點:

  • 1.使程式碼更加清晰可讀,減少繁多的if-else
  • 2.功能更易擴充,可維護,健壯性【單一指責,抽離變與不變。如果你看了上篇單例設計模式你就看到我也強調過這一點】
  • 3.策略可重用

策略模式的應用場景:

場景一:

工資計算

財務妹妹想要給技術猛男、鼓勵師妹妹、冷血產品經理來統計他們本月的工資。但他們的工資計算方式不一樣呀,這可咋整?

  • 技術猛男:(10000基本+1000專案獎勵)* 1.2績效
  • 鼓勵師妹妹:(9000基本)*1績效
  • 冷血產品經理:(500基本)*1.5績效
  • 【績效每月都不同】

本著抽離變與不變的思路:

  • 變:不同角色不同計算方式
  • 不變:都需要計算工資

面對物件思路的寫法:

<script>

  // 策略類 定義各個角色的工資計算方式
  function WageStrategy() {
  
  }
  WageStrategy.prototype.ROLE = {
    IT: 'itHandsomeBoy',
    ENCOURAGE: 'encourager',
    MANAGER: 'productManager',
  };
  WageStrategy.prototype.itHandsomeBoy = function (performance) {
    return (10000 + 2000) * performance;
  }
  WageStrategy.prototype.encourager = function (performance) {
    return (9000) * performance;
  }
  WageStrategy.prototype.productManager = function (performance) {
    return (500) * performance;
  }
  WageStrategy.prototype.getRoleWage = function (role, performance) {
    return this[role](performance);
  }

  // 中間類 (發起計算的財務)【持有策略物件】
  function FinancialMan(strategy) {
    this.strategy = strategy;
  }
  FinancialMan.prototype.getWage = function (role, performance) {
    return this.strategy.getRoleWage(role, performance);
  };

  // 執行
  let financialMan = new FinancialMan(new WageStrategy());
  console.log(financialMan.getWage(financialMan.strategy.ROLE.IT, 1.2));
  console.log(financialMan.getWage(financialMan.strategy.ROLE.ENCOURAGE, 1));
  console.log(financialMan.getWage(financialMan.strategy.ROLE.MANAGER, 1.5));

</script>

上面是模擬像Java這樣基於類的語言,而js中存在很多不一樣的特性,例如:函式即物件!所以以下是改進的
Js版本的策略模式。

<script>

  // 策略物件,封裝工資演算法
  let wageStrategy = {

    ROLE : {
      IT : 'itHansomeBoy',
      ENCOURAGE : 'encourager',
      MANAGER : 'productManager',
    },

    itHansomeBoy: function (performance) {
      return (10000 + 2000) * performance;
    },
    encourager: function (performance) {
      return (9000) * performance;
    },
    productManager: function (performance) {
      return (500) * performance;
    },
  };

  // 計算工資物件
  let getWage= function (role, performance) {
      return wageStrategy[role](performance);
	}
  

  console.log(getWage(wageStrategy.ROLE.IT, 1.2));          // 14400
  console.log(getWage(wageStrategy.ROLE.ENCOURAGE, 1));     // 9000
  console.log(getWage(wageStrategy.ROLE.MANAGER, 1.5));     // 750

</script>

場景二:

表單驗證(註冊賬號)
使用者在填完一堆資訊後【如:手機號、郵箱號、姓名、密碼、驗證碼】,點選註冊,此時應該先對每個欄位進行檢查,都通過後才能提交到後臺。

比較猛的寫法:

let register = function () {
	let name, phone, 
	if(!name) {
		return;
	}else if (!phone || phone.length !== 11) {
		return;
	}
	// do register request
}

隨著需要檢驗的欄位越來越多,那麼else if的邏輯越來越重。並且,如果有個完善資訊頁面,同樣需要用到手機號,郵箱號這些資訊檢測怎麼複用勒?

那麼,策略模式來了:

<script>

  // 表單檢驗策略類
  let checkInfoStrategy = {

    phone: function (phone) {
      let pass = true;
      let tips = '';
      if (!phone) {
        pass = false;
        tips = '手機號不能為空';
      }
      return { pass, tips };
    },

    email: function (email) {
      let pass = true;
      let tips = '';
      if (!email) {
        pass = false;
        tips = '郵箱不能為空';
      } else if (email.indexOf('@') < 0) {
        pass = false;
        tips = '郵箱格式不正確';
      }
      return { pass, tips };
    }
  }

  // 中間者 發起表單檢驗
  let Checker = function () {
    this.cache = [];  // 存放需要檢驗項:{策略,待檢驗字元}

    // 新增待檢查項
    this.add = function (stragetyItem, beCheckInfo) {
      this.cache.push({ item: stragetyItem, info: beCheckInfo });
      return this;
    };

    // 開始檢查
    this.startCheck = function () {
      let result = { pass : true, tips : '' };
      for (let i = 0; i < this.cache.length; i++) {
        let checkItem = this.cache[i];
        let {pass, tips} = checkItem.item(checkItem.info);
        result.pass = pass;
        result.tips = tips;
        if (!pass) {
          break;
        }
      }
      return result;
    };
  }

  // 執行
  let phone = '18826274139';
  let email = '';
  let checker = new Checker();
  checker.add(checkInfoStrategy.phone, phone).add(checkInfoStrategy.email, email);
  let { pass, tips } = checker.startCheck();
  console.log(':::' + pass, tips); // :::false 郵箱格式不正確

</script>

其他不多說了,一定要動過。
思路就是抽離變與不變。策略類之放不同的演算法/邏輯,使用中間類來協助發起策略類的使用。

相關文章