大部分講設(shè)計(jì)模式的文章都是使用的 Java、C++ 這樣的以類(lèi)為基礎(chǔ)的靜態(tài)類(lèi)型語(yǔ)言,作為前" />

国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

15158846557 在線咨詢(xún) 在線咨詢(xún)
15158846557 在線咨詢(xún)
所在位置: 首頁(yè) > 營(yíng)銷(xiāo)資訊 > 網(wǎng)站運(yùn)營(yíng) > 前端的設(shè)計(jì)模式系列-策略模式

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

時(shí)間:2023-10-07 19:18:02 | 來(lái)源:網(wǎng)站運(yùn)營(yíng)

時(shí)間:2023-10-07 19:18:02 來(lái)源:網(wǎng)站運(yùn)營(yíng)

前端的設(shè)計(jì)模式系列-策略模式:代碼也寫(xiě)了幾年了,設(shè)計(jì)模式處于看了忘,忘了看的狀態(tài),最近對(duì)設(shè)計(jì)模式有了點(diǎn)感覺(jué),索性就再學(xué)習(xí)總結(jié)下吧。

大部分講設(shè)計(jì)模式的文章都是使用的 JavaC++ 這樣的以類(lèi)為基礎(chǔ)的靜態(tài)類(lèi)型語(yǔ)言,作為前端開(kāi)發(fā)者,js 這門(mén)基于原型的動(dòng)態(tài)語(yǔ)言,函數(shù)成為了一等公民,在實(shí)現(xiàn)一些設(shè)計(jì)模式上稍顯不同,甚至簡(jiǎn)單到不像使用了設(shè)計(jì)模式,有時(shí)候也會(huì)產(chǎn)生些困惑。

下面按照「場(chǎng)景」-「設(shè)計(jì)模式定義」- 「優(yōu)化代碼」- 「更多場(chǎng)景」-「總」的順序來(lái)總結(jié)一下,如有不當(dāng)之處,歡迎交流討論。

場(chǎng)景

進(jìn)入一個(gè)營(yíng)銷(xiāo)活動(dòng)頁(yè)面,會(huì)根據(jù)后端下發(fā)的不同 type ,前端頁(yè)面展示不同的彈窗。

async getMainData() { try { const res = await activityQuery(); // 請(qǐng)求后端數(shù)據(jù) this.styleType = res.styleType; if (this.styleType === STYLE_TYPE.Reward) { this.openMoneyPop(); }else if (this.styleType === STYLE_TYPE.Waitreward) { this.openShareMoneyPop(); } else if (this.styleType === STYLE_TYPE.Poster) { this.openPosterPop(); } else if (this.styleType === STYLE_TYPE.Activity) { this.openActivityPop(); } else if (this.styleType === STYLE_TYPE.Balance) { this.openBalancePop(); } else if (this?.styleType === STYLE_TYPE.Cash) { this.openCashBalancePop(); } } catch (error) { log.error(MODULENAME, '主接口異常', JSON.stringify(error)); }}這個(gè)代碼的話(huà)看了就想打人,未來(lái)新增一種彈窗類(lèi)型的話(huà),我們需要到 getMainData 內(nèi)部去補(bǔ)一個(gè) else if,一不小心可能就會(huì)影響到原有的邏輯,并且隨著迭代函數(shù)會(huì)越來(lái)越大。但其實(shí)每種彈窗是相互獨(dú)立的,我們并不關(guān)心其他彈窗的邏輯。

此時(shí),就需要策略模式了。

策略模式

看下 維基百科 的定義。

策略模式作為一種軟件設(shè)計(jì)模式,指對(duì)象有某個(gè)行為,但是在不同的場(chǎng)景中,該行為有不同的實(shí)現(xiàn)算法。比如每個(gè)人都要“交個(gè)人所得稅”,但是“在中國(guó)交個(gè)人所得稅”和“在美國(guó)交個(gè)人所得稅”就有不同的算稅方法。
策略模式:
看一下如果是 Java 語(yǔ)言會(huì)怎么實(shí)現(xiàn):

//StrategyExample test applicationclass StrategyExample { public static void main(String[] args) { Context context; // Three contexts following different strategies context = new Context(new FirstStrategy()); context.execute(); context = new Context(new SecondStrategy()); context.execute(); context = new Context(new ThirdStrategy()); context.execute(); }}// The classes that implement a concrete strategy should implement this// The context class uses this to call the concrete strategyinterface Strategy { void execute();}// Implements the algorithm using the strategy interfaceclass FirstStrategy implements Strategy { public void execute() { System.out.println("Called FirstStrategy.execute()"); }}class SecondStrategy implements Strategy { public void execute() { System.out.println("Called SecondStrategy.execute()"); }}class ThirdStrategy implements Strategy { public void execute() { System.out.println("Called ThirdStrategy.execute()"); }}// Configured with a ConcreteStrategy object and maintains a reference to a Strategy objectclass Context { Strategy strategy; // Constructor public Context(Strategy strategy) { this.strategy = strategy; } public void execute() { this.strategy.execute(); }}主要是利用到類(lèi)的多態(tài),根據(jù)傳入 Context 中不同的 strategy,來(lái)執(zhí)行不同的 execute()。如果未來(lái)有新增算法的話(huà),只需要新增一個(gè)類(lèi)即可。

那如果是 js 呢?眾所周知,ES6 之前 js 是沒(méi)有 class 關(guān)鍵字的,即使現(xiàn)在有了,也依然只是基于原型的語(yǔ)法糖,底層和 java 的類(lèi)是完全不同的。

此外,js 中函數(shù)是一等公民,可以當(dāng)作參數(shù)傳入和返回,因此實(shí)現(xiàn)策略模式我們完全不需要去定一個(gè)類(lèi),然后通過(guò)生成的對(duì)象調(diào)用方法。在 js 中我們只需要將函數(shù)傳入即可。

const strategies = { FirstStrategy() { console.log("Called FirstStrategy"); }, SecondStrategy() { console.log("Called SecondStrategy"); }, ThirdStrategy() { console.log("Called ThirdStrategy"); }}const execute = (strategy) => { return strategies[strategy]();}execute('FirstStrategy')execute('SecondStrategy')execute('ThirdStrategy')上邊主要演示了思想,實(shí)際開(kāi)發(fā)中,我們完全可以把每種策略分文件單獨(dú)寫(xiě)然后再 import。

相對(duì)于 java,寫(xiě)法簡(jiǎn)單了很多,我們不需要定義各個(gè)類(lèi),只需要用一個(gè)對(duì)象來(lái)存儲(chǔ)所有策略,再提供一個(gè)調(diào)用策略的函數(shù),甚至這個(gè)函數(shù)也可以直接省略。

優(yōu)化代碼

將所有彈窗方法從業(yè)務(wù)代碼 getMainData 中抽離出來(lái),只暴露一個(gè)打開(kāi)彈窗的函數(shù)供業(yè)務(wù)調(diào)用。

import { openPop } from './popTypes';async getMainData() { try { const res = await activityQuery(); // 請(qǐng)求后端數(shù)據(jù) openPop(res.styleType) } catch (error) { log.error(MODULENAME, '主接口異常', JSON.stringify(error)); }}然后就是 popTypes.js 文件。

import { SHARETYPE } from './constant';const popTypes = { [STYLE_TYPE.Reward]: function() { ... }, [STYLE_TYPE.Waitreward]: function() { ... }, [STYLE_TYPE.Poster]: function() { ... }, [STYLE_TYPE.Activity]: function() { ... }, [STYLE_TYPE.Balance]: function() { ... }, [STYLE_TYPE.Cash()]: function() { ... },}export function openPop(type){ return popTypes[type]();}

更多場(chǎng)景

表單驗(yàn)證也是一個(gè)典型場(chǎng)景,常用的,我們需要驗(yàn)證用戶(hù)輸入字段是否是數(shù)字、是否必填、是否是數(shù)組,還有自定義的一些驗(yàn)證,同樣可以通過(guò)策略模式實(shí)現(xiàn),從而使得代碼更易維護(hù)和擴(kuò)展。

如果使用過(guò) Element UI,對(duì)下邊表單的 rule 一定很熟悉。

const rule = { name: { type: 'string', required: true, message: '請(qǐng)輸入名字' }, age: [ { type: 'number', message: '請(qǐng)輸入number', }, { message: '年齡必須大于 18', validator: (rule, value) => value > 18, }, ]};Element 會(huì)幫助我們校驗(yàn) name 是否是 string、age 是否是 number。而 Element 其實(shí)是用的一個(gè)開(kāi)源的 async-validator 校驗(yàn)庫(kù)。

async-validator 內(nèi)部會(huì)內(nèi)置很多 typevalidator,然后會(huì)根據(jù) rule 中的 type 來(lái)幫我們填充相應(yīng)的 validator。讓我們看一下相應(yīng)的源碼。

首先是 validator 文件夾,會(huì)定義很多校驗(yàn)規(guī)則,date 類(lèi)型、number 類(lèi)型等等,相當(dāng)于很多策略。







然后是上邊截圖中的 validator/index.ts 文件,將這些策略導(dǎo)出。

import string from './string';import method from './method';import number from './number';import boolean from './boolean';import regexp from './regexp';import integer from './integer';import float from './float';import array from './array';import object from './object';import enumValidator from './enum';import pattern from './pattern';import date from './date';import required from './required';import type from './type';import any from './any';export default { string, method, number, boolean, regexp, integer, float, array, object, enum: enumValidator, pattern, date, url: type, hex: type, email: type, required, any,};校驗(yàn)前會(huì)執(zhí)行下邊的代碼,通過(guò) type 填充相應(yīng)的 validator。

arr.forEach(r => { ... if (typeof rule === 'function') { rule = { validator: rule, }; } else { rule = { ...rule }; } // Fill validator. Skip if nothing need to validate rule.validator = this.getValidationMethod(rule); // 策略模式應(yīng)用 if (!rule.validator) { return; } ...});});策略模式的體現(xiàn)就是 getValidationMethod 方法了,讓我們看一下實(shí)現(xiàn)。

import validators from './validator/index'; // 導(dǎo)入所有策略getValidationMethod(rule: InternalRuleItem) { // 已經(jīng)有了就直接返回 validator if (typeof rule.validator === 'function') { return rule.validator; } ... // 通過(guò) type 得到相應(yīng)的 validator。 return validators[this.getType(rule)] || undefined;}getType(rule: InternalRuleItem) { ... return rule.type || 'string';}填充相應(yīng)的 validator 之后接下來(lái)只需要遍歷相應(yīng)的 rule 然后校驗(yàn)就可以了。

當(dāng)出現(xiàn)很多 if else 或者 switch 的時(shí)候,我們就可以考慮是否能使用策略模式了。

通過(guò)策略模式,我們可以把策略從業(yè)務(wù)代碼中抽離出來(lái),未來(lái)擴(kuò)展的話(huà)無(wú)需深入到業(yè)務(wù)代碼修改,只需要新增需要的策略,不會(huì)使得業(yè)務(wù)代碼變得越來(lái)越臃腫。

甚至策略模式也可以更好的進(jìn)行復(fù)用,如果其他業(yè)務(wù)場(chǎng)景需要類(lèi)似的策略,直接引入即可,和原有的業(yè)務(wù)相互獨(dú)立。

關(guān)鍵詞:模式,系列,策略,設(shè)計(jì),端的

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點(diǎn)擊下載Chrome瀏覽器
關(guān)閉