前端設(shè)計(jì)模式-策略模式

歡迎訪問主頁,有更多文章內(nèi)容
轉(zhuǎn)載請(qǐng)注明原出處
原文鏈接地址:前端設(shè)計(jì)模式-策略模式

前言

策略模式是定義一系列的算法,把它們一個(gè)個(gè)封裝起來瑞凑,并且使它們可以相互替換。將不變的部分和變化的部分隔開是每個(gè)設(shè)計(jì)模式的主題砂碉。策略模式的目的就是使算法的使用與算法分離開來五辽。封裝的算法具有一定的獨(dú)立性,不會(huì)隨客戶端的變化而變化蹋岩。

一個(gè)策略模式的程序至少有兩部分組成赖草。第一部分是策略類,策略類封裝了具體的算法剪个,負(fù)責(zé)具體的計(jì)算過程秧骑。 第二部分是環(huán)境類Context,它接受了客戶的要求扣囊,隨后吧請(qǐng)求委托給某一具體的策略類乎折。要做到這點(diǎn),Context中要維持對(duì)摸個(gè)策略對(duì)象的引用。

例子

這個(gè) demo 是一個(gè)計(jì)算的代碼段

function count(type, number1, number2) {
  switch (type) {
    case "add":
      return number1 + number2;
    case "subtract":
      return number1 - number2;
    case "multiply":
      return number1 * number2;
    default:
      return number1 / number2;
  }
}
// if else
function count1(type, number1, number2) {
  if (type === "add") {
    return number1 + number2;
  } else if (type === "subtract") {
    return number1 - number2;
  } else if (type === "multiply") {
    return number1 * number2;
  } else {
    return number1 / number2;
  }
}

或者這中間的switchif...else...來實(shí)現(xiàn)侵歇,但switchif...else...更加一目了然骂澄。這段計(jì)算方法本身沒有問題,但是有更加便于維護(hù)和擴(kuò)展的實(shí)現(xiàn)方式惕虑。下面來進(jìn)行改進(jìn)

const count = {
  add(number1, number2) {
    return number1 + number2;
  },
  subtract(number1, number2) {
    return number1 - number2;
  },
  multiply(number1, number2) {
    return number1 * number2;
  },
  divide(number1, number2) {
    return number1 / number2;
  },
};

count.add(2, 3); // 5

count.subtract(10, 3); // 7

count.multiply(2, 3); // 6

count.divide(6, 2); // 3

經(jīng)過改造后酗洒,可以看成是一個(gè)策略模式,接下來看個(gè)復(fù)雜的例子枷遂。商場的會(huì)員分普通會(huì)員樱衷、銀卡會(huì)員、金卡會(huì)員酒唉、鉆石會(huì)員矩桂、至尊會(huì)員等級(jí)別,在商場消費(fèi)享不同的折扣和積分。會(huì)員在同一商品的折扣分別是 98侄榴、95雹锣、9、8.8癞蚕、8.5 折優(yōu)惠蕊爵。積分比例分別是 1、1.2桦山、1.5攒射、1.6、1.8恒水。購買商品對(duì)應(yīng)的會(huì)員折扣和積分獲取用策略模式來實(shí)現(xiàn):

const priceStrategy = {
  level1(price) {
    return price * (98 / 100);
  },
  level2(price) {
    return price * (95 / 100);
  },
  level3(price) {
    return price * (90 / 100);
  },
  level4(price) {
    return price * (88 / 100);
  },
  level5(price) {
    return price * (85 / 100);
  },
};

const cumulativeScoreStrategy = {
  level1(price) {
    return Math.floor(price);
  },
  level2(price) {
    return Math.floor(price * 1.1);
  },
  level3(price) {
    return Math.floor(price * 1.2);
  },
  level4(price) {
    return Math.floor(price * 1.3);
  },
  level5(price) {
    return Math.floor(price * 1.4);
  },
};

function calculatePrice(level, price) {
  return priceStrategy[level] ? priceStrategy[level](price) : 0;
}

function calculateScore(level, price) {
  const actually = calculatePrice(level, price);
  return (
    cumulativeScoreStrategy[level] && cumulativeScoreStrategy[level](actually)
  );
}

//  一臺(tái)的彩電的價(jià)格是 8888会放,鉆石會(huì)員的折后價(jià)
calculatePrice("level4", 8888); // 7554.8
// 獲得的積分
calculateScore("level4", 8888); // 10567

表單驗(yàn)證的策略模式

const strategies = {
  isCorrectPassword(value, errorMsg) {
    if (!/^(?:(?=.*[A-Z])(?=.*[0-9])).\\S{7,19}$/.test(value)) {
      return errorMsg;
    }
    return null;
  },
  isNotEmpty(value, errorMsg) {
    if (value === "" || value === undefined) {
      return errorMsg;
    }
    return null;
  },
  minLength(value, length, errorMsg) {
    if (value.length < length) {
      return errorMsg;
    }
    return null;
  },
  maxLength(value, length, errorMsg) {
    if (value.length > length) {
      return errorMsg;
    }
    return null;
  },
  isMobile(value, errorMsg) {
    if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
      return errorMsg;
    }
    return null;
  },
  isNotAllEmpty(value = [], errorMsg) {
    if (!value.some((i) => !!i)) {
      return errorMsg;
    }
    return null;
  },
};

class Validator {
  constructor() {
    this.cache = [];
  }

  add(value, rules) {
    for (let i = 0, rule; (rule = rules[i++]); ) {
      const strategyArray = rule.strategy.split(":") || [];
      const { errorMsg } = rule;
      this.cache.push(() => {
        const strategy = strategyArray.shift();
        strategyArray.unshift(value);
        strategyArray.push(errorMsg);
        return strategies[strategy].apply(value, strategyArray);
      });
    }
  }

  run() {
    for (let i = 0, validatorFunc; (validatorFunc = this.cache[i++]); ) {
      const msg = validatorFunc();
      if (msg) {
        return msg;
      }
    }
    return null;
  }
}

// 使用
const validator = new Validator();
validator.add(registerForm.userName, [
  {
    strategy: "isNonEmpty",
    errorMsg: "userName not empty!",
  },
  {
    strategy: "minLength:6",
    errorMsg: "userName length should more than 6",
  },
]);
validator.add(registerForm.password, [
  {
    strategy: "minLength:6",
    errorMsg: "password length should more than 6",
  },
]);

const errorMsg = validator.start();

if (errorMsg) {
  console.log(errorMsg);
  return errorMsg;
}

策略模式屬于對(duì)象行為型模式,主要針對(duì)一組算法钉凌,將每一個(gè)算法封裝到具有共同接口的獨(dú)立的類中咧最,從而使得它們可以相互替換。策略模式使得算法可以在不影響 到客戶端的情況下發(fā)生變化御雕。通常矢沿,策略模式適用于當(dāng)一個(gè)應(yīng)用程序需要實(shí)現(xiàn)一種特定的服務(wù)或者功能,而且該程序有多種實(shí)現(xiàn)方式時(shí)使用酸纲。

策略模式的優(yōu)缺點(diǎn)

策略模式是一種常用且有效的得設(shè)計(jì)模式捣鲸,

優(yōu)點(diǎn)

· 減少重復(fù)代碼 策略模式利用組合、委托和多態(tài)等技術(shù)和思想福青,減少很多模板代碼。

· 擴(kuò)展性 策略模式提供了對(duì)開放-封閉原則的完美支持脓诡,將算法封裝在獨(dú)立的策略類里面无午,使它們易于切換,易于理解祝谚,易于擴(kuò)展宪迟。

· 在策略模式中的算法利用組合和委托讓Context擁有執(zhí)行算法的能力,這也是繼承的一種更輕便的替換方案交惯。

缺點(diǎn)

· 使用策略模式會(huì)的程序增加了許多策略類或者策略對(duì)象次泽,但實(shí)際上這比把他們負(fù)責(zé)的邏輯堆砌在Context中要更好。

· 要使用策略模式席爽,必須了解所有的策略意荤,必須了解各個(gè)策略之間的不同點(diǎn),因?yàn)楦鞣N算法之間相互獨(dú)立只锻,對(duì)于復(fù)雜的算法處理相同的邏輯無法實(shí)現(xiàn)共享玖像。

參考

本文參考<<javascript 設(shè)計(jì)模式與開發(fā)實(shí)踐>> 推薦閱讀。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末齐饮,一起剝皮案震驚了整個(gè)濱河市捐寥,隨后出現(xiàn)的幾起案子笤昨,更是在濱河造成了極大的恐慌,老刑警劉巖握恳,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞒窒,死亡現(xiàn)場離奇詭異,居然都是意外死亡乡洼,警方通過查閱死者的電腦和手機(jī)崇裁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來就珠,“玉大人寇壳,你說我怎么就攤上這事∑拊酰” “怎么了壳炎?”我有些...
    開封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長逼侦。 經(jīng)常有香客問我匿辩,道長,這世上最難降的妖魔是什么榛丢? 我笑而不...
    開封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任铲球,我火速辦了婚禮,結(jié)果婚禮上晰赞,老公的妹妹穿的比我還像新娘稼病。我一直安慰自己,他們只是感情好掖鱼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開白布然走。 她就那樣靜靜地躺著,像睡著了一般戏挡。 火紅的嫁衣襯著肌膚如雪芍瑞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天褐墅,我揣著相機(jī)與錄音拆檬,去河邊找鬼。 笑死妥凳,一個(gè)胖子當(dāng)著我的面吹牛竟贯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播逝钥,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼澄耍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起齐莲,我...
    開封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤痢站,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后选酗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阵难,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年芒填,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了呜叫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡殿衰,死狀恐怖朱庆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情闷祥,我是刑警寧澤娱颊,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站凯砍,受9級(jí)特大地震影響箱硕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜悟衩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一剧罩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧座泳,春花似錦惠昔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至薛耻,卻和暖如春营罢,著一層夾襖步出監(jiān)牢的瞬間赏陵,已是汗流浹背饼齿。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蝙搔,地道東北人缕溉。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像吃型,于是被迫代替她去往敵國和親证鸥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 行為型模式用于描述程序在運(yùn)行時(shí)復(fù)雜的流程控制,即描述多個(gè)類或?qū)ο笾g怎樣相互協(xié)作共同完成單個(gè)對(duì)象都無法單獨(dú)完成的任...
    放下梧菲閱讀 284評(píng)論 0 1
  • 目錄 本文的結(jié)構(gòu)如下: 引言 什么是策略模式 模式的結(jié)構(gòu) 典型代碼 代碼示例 策略模式和模板方法模式的區(qū)別 優(yōu)點(diǎn)和...
    w1992wishes閱讀 860評(píng)論 1 7
  • 策略模式的介紹 ? 在實(shí)際開發(fā)過程中枉层,我們常常遇到這樣的問題泉褐,實(shí)現(xiàn)某一個(gè)功能可以有多種算法或者策略,我們根據(jù)實(shí)...
    Android天之驕子閱讀 458評(píng)論 0 0
  • 一.定義 定義一系列的算法,把它們一個(gè)個(gè)封裝起來, 并且使它們可相互替換鸟蜡,更詳細(xì)一點(diǎn)就是封裝可交換的行為膜赃,并使用委...
    小巨人Vea閱讀 437評(píng)論 0 0
  • 在程序設(shè)計(jì)中,我們也常常遇到類似的情況揉忘,要實(shí)現(xiàn)某一個(gè)功能有多種方案可以選擇跳座。比如一個(gè)壓縮文件的程序,既可以選擇zi...
    yufawu閱讀 369評(píng)論 0 3