時(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é)下吧。Java
、C++
這樣的以類(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)生些困惑。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è)計(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
。java
,寫(xiě)法簡(jiǎn)單了很多,我們不需要定義各個(gè)類(lèi),只需要用一個(gè)對(duì)象來(lái)存儲(chǔ)所有策略,再提供一個(gè)調(diào)用策略的函數(shù),甚至這個(gè)函數(shù)也可以直接省略。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]();}
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ù)。type
的 validator
,然后會(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)就可以了。if else
或者 switch
的時(shí)候,我們就可以考慮是否能使用策略模式了。關(guān)鍵詞:模式,系列,策略,設(shè)計(jì),端的
客戶(hù)&案例
營(yíng)銷(xiāo)資訊
關(guān)于我們
客戶(hù)&案例
營(yíng)銷(xiāo)資訊
關(guān)于我們
微信公眾號(hào)
版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。