面試官:說說你對策略模式的理解?應(yīng)用場景?
一、策略模式是什么
策略模式(Strategy Pattern)指的是定義一系列的算法,把它們一個(gè)個(gè)封裝起來,目的就是將算法的使用與算法的實(shí)現(xiàn)分離開來
一個(gè)基于策略模式的程序至少由兩部分組成:
策略類,策略類封裝了具體的算法,并負(fù)責(zé)具體的計(jì)算過程
環(huán)境類Context,Context 接受客戶的請求,隨后 把請求委托給某一個(gè)策略類
二、使用
舉個(gè)例子,公司的年終獎(jiǎng)是根據(jù)員工的工資和績效來考核的,績效為A的人,年終獎(jiǎng)為工資的4倍,績效為B的人,年終獎(jiǎng)為工資的3倍,績效為C的人,年終獎(jiǎng)為工資的2倍
若使用if來實(shí)現(xiàn),代碼則如下:
- var calculateBouns = function(salary,level) {
- if(level === 'A') {
- return salary * 4;
- }
- if(level === 'B') {
- return salary * 3;
- }
- if(level === 'C') {
- return salary * 2;
- }
- };
- // 調(diào)用如下:
- console.log(calculateBouns(4000,'A')); // 16000
- console.log(calculateBouns(2500,'B')); // 7500
從上述可有看到,函數(shù)內(nèi)部包含過多if...else,并且后續(xù)改正的時(shí)候,需要在函數(shù)內(nèi)部添加邏輯,違反了開放封閉原則
而如果使用策略模式,就是先定義一系列算法,把它們一個(gè)個(gè)封裝起來,將不變的部分和變化的部分隔開,如下:
- var obj = {
- "A": function(salary) {
- return salary * 4;
- },
- "B" : function(salary) {
- return salary * 3;
- },
- "C" : function(salary) {
- return salary * 2;
- }
- };
- var calculateBouns =function(level,salary) {
- return obj[level](salary);
- };
- console.log(calculateBouns('A',10000)); // 40000
上述代碼中,obj對應(yīng)的是策略類,而calculateBouns對應(yīng)上下通信類
又比如實(shí)現(xiàn)一個(gè)表單校驗(yàn)的代碼,常常會(huì)像如下寫法:
- var registerForm = document.getElementById("registerForm");
- registerForm.onsubmit = function(){
- if(registerForm.userName.value === '') {
- alert('用戶名不能為空');
- return;
- }
- if(registerForm.password.value.length < 6) {
- alert("密碼的長度不能小于6位");
- return;
- }
- if(!/(^1[3|5|8][0-9]{9}$)/.test(registerForm.phoneNumber.value)) {
- alert("手機(jī)號(hào)碼格式不正確");
- return;
- }
- }
上述代碼包含多處if語句,并且違反了開放封閉原則,如果應(yīng)用中還有其他的表單,需要重復(fù)編寫代碼
此處也可以使用策略模式進(jìn)行重構(gòu)校驗(yàn),第一步確定不變的內(nèi)容,即策略規(guī)則對象,如下:
- var strategy = {
- isNotEmpty: function(value,errorMsg) {
- if(value === '') {
- return errorMsg;
- }
- },
- // 限制最小長度
- minLength: function(value,length,errorMsg) {
- if(value.length < length) {
- return errorMsg;
- }
- },
- // 手機(jī)號(hào)碼格式
- mobileFormat: function(value,errorMsg) {
- if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
- return errorMsg;
- }
- }
- };
然后找出變的地方,作為環(huán)境類context,負(fù)責(zé)接收用戶的要求并委托給策略規(guī)則對象,如下Validator類:
- var Validator = function(){
- this.cache = []; // 保存效驗(yàn)規(guī)則
- };
- Validator.prototype.add = function(dom,rule,errorMsg) {
- var str = rule.split(":");
- this.cache.push(function(){
- // str 返回的是 minLength:6
- var strategy = str.shift();
- str.unshift(dom.value); // 把input的value添加進(jìn)參數(shù)列表
- str.push(errorMsg); // 把errorMsg添加進(jìn)參數(shù)列表
- return strategys[strategy].apply(dom,str);
- });
- };
- Validator.prototype.start = function(){
- for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {
- var msg = validatorFunc(); // 開始效驗(yàn) 并取得效驗(yàn)后的返回信息
- if(msg) {
- return msg;
- }
- }
- };
通過validator.add方法添加校驗(yàn)規(guī)則和錯(cuò)誤信息提示,使用如下:
- var validateFunc = function(){
- var validator = new Validator(); // 創(chuàng)建一個(gè)Validator對象
- /* 添加一些效驗(yàn)規(guī)則 */
- validator.add(registerForm.userName,'isNotEmpty','用戶名不能為空');
- validator.add(registerForm.password,'minLength:6','密碼長度不能小于6位');
- validator.add(registerForm.userName,'mobileFormat','手機(jī)號(hào)碼格式不正確');
- var errorMsg = validator.start(); // 獲得效驗(yàn)結(jié)果
- return errorMsg; // 返回效驗(yàn)結(jié)果
- };
- var registerForm = document.getElementById("registerForm");
- registerForm.onsubmit = function(){
- var errorMsg = validateFunc();
- if(errorMsg){
- alert(errorMsg);
- return false;
- }
- }
上述通過策略模式完成表單的驗(yàn)證,并且可以隨時(shí)調(diào)用,在修改表單驗(yàn)證規(guī)則的時(shí)候,也非常方便,通過傳遞參數(shù)即可調(diào)用
三、應(yīng)用場景
從上面可以看到,使用策略模式的優(yōu)點(diǎn)有如下:
- 策略模式利用組合,委托等技術(shù)和思想,有效的避免很多if條件語句
- 策略模式提供了開放-封閉原則,使代碼更容易理解和擴(kuò)展
- 策略模式中的代碼可以復(fù)用
策略模式不僅僅用來封裝算法,在實(shí)際開發(fā)中,通常會(huì)把算法的含義擴(kuò)散開來,使策略模式也可以用來封裝 一系列的“業(yè)務(wù)規(guī)則”
只要這些業(yè)務(wù)規(guī)則指向的目標(biāo)一致,并且可以被替換使用,我們就可以用策略模式來封裝它們
參考文獻(xiàn)
https://segmentfault.com/a/1190000021883055
https://juejin.cn/post/6844903504109109262
https://juejin.cn/post/6844903751225081864