В последнее время я наблюдаю за шаблонами проектирования Javascript и хочу улучшить свое понимание шаблона стратегии, написав статьи, и в то же время записать свой опыт обучения. Я надеюсь, что после прочтения, если вы почувствуете, что что-то приобрели, вы сможете помочь и поднять большой палец вверх, чтобы показать свою поддержку.
Определение шаблона стратегии
Определение шаблона стратегии таково: определите серию алгоритмов, инкапсулируйте их один за другим и сделайте их взаимозаменяемыми.
Каждый раз, когда я сталкиваюсь с определением этого шаблона проектирования, я всегда смущаюсь на первый взгляд и не знаю, что сказать. На самом деле есть способ:Три последовательных вопроса о качестве. Мы можем уточнить определение, затем проанализировать значение каждого поля и объединить их, чтобы понять, что на самом деле хочет выразить определение.
Поэтому для определения режима стратегии мы можем прийти к волне из трех последовательных вопросов о качестве:
- здесьалгоритмо чем это
- Зачем тебе это нужноупаковкаВстаньте
- заменяют друг другачто ты имеешь в виду
Прежде чем ответить на три вопроса о качестве, мы можем начать с жизни, чтобы увидеть сценарии применения режима стратегии. На самом деле, мы можем использовать разные стратегии для решения проблем во многих сценариях.
путешествовать
- Если вы местный тиран или у вас мало времени, вы можете выбрать рейс
- Если вы живете в мелкой буржуазии и никуда не торопитесь, вы можете воспользоваться высокоскоростной железной дорогой.
- Если вы плохой путешественник, то можете ездить на велосипеде и т.д.
Сжатый файл
- почтовый алгоритм
- gzip-алгоритм
Из этого видно, что наша стратегия на самом деле является методом решения нашей проблемы, который мы можем определить как алгоритм.
Применение шаблона стратегии
Сказав так много, на самом деле, что всех больше всего волнует, так это сценарий применения шаблона стратегии. Далее мы используем пример награды на конец года, чтобы шаг за шагом решить наши три последовательных вопроса о качестве. Премия по итогам года присуждается в зависимости от базовой заработной платы сотрудника и результатов работы на конец года. Например, премия на конец года с производительностью S в 4 раза больше оклада, премия на конец года с производительностью A в 3 раза больше оклада, а премия на конец года с производительностью B может быть только в 2 раза больше оклада. Эту логику мы можем реализовать с помощью базового кода
var calculateBonus = function (performanceLevel, salary) {
if (performanceLevel === 'S') {
return salary * 4;
};
if (performanceLevel === 'A') {
return salary * 3;
};
if (performanceLevel === 'B') {
return salary * 2;
};
}
//获得绩效为B的员工
calculateBonus('B', 20000);
//获得绩效为S的员工
calculateBonus('S', 6000);
Очевидно, что хотя этот код реализует нужную нам функцию, он очень ограничен и имеет следующие недостатки:
- Содержит слишком много операторов if-else, что делает функцию слишком большой
- Функции calculateBonus не хватает гибкости, если нужно добавить разные уровни производительности, нужно менять ее внутреннюю реализацию, что нарушает принцип открытости-закрытости.
- Алгоритм имеет плохую возможность повторного использования, если это правило расчета нужно использовать где-то еще, его можно только повторно вывести (копировать, вставить)
Оптимизация первая (комбинаторная функция)
Мы можем реорганизовать этот код, объединив функции, инкапсулировать различные алгоритмы (то есть правила расчета годовой премии) в независимые небольшие функции и дать хорошее имя, тогда мы сможем решить указанные выше недостатки. 3 Проблема повторного использования алгоритма. код показывает, как показано ниже
var performanceS = function (salary) {
return salary * 4;
};
var performanceA = function (salary) {
return salary * 3;
};
var performanceB = function (salary) {
return salary * 2;
};
var calculateBonus = function ( performanceLevel, salary) {
if ( performanceLevel === 'S' ) {
return performanceS( salary );
};
if ( performanceLevel === 'A' ) {
return performanceA( salary );
};
if ( performanceLevel === 'B' ) {
return performanceB( salary );
};
}
calculateBonus( 'A', 10000 );
Комбинируя функции, мы видим, что наш алгоритм инкапсулирован в небольшие функции, которые могут решить проблему повторного использования функций. Однако наша основная проблема, то есть вышеупомянутые недостатки 1 и 2, не была решена, поэтому мы продолжаем совершенствоваться, на этот раз через режим стратегии для оптимизации.
Оптимизация II (режим стратегии)
Сначала мы должны пониматьЧасть того же раздела и изменения - это тема разделения каждого шаблона проектирования., а цель шаблона стратегии состоит в том, чтобыОтделение использования алгоритма от реализации алгоритма. Тогда в этом примере способ использования алгоритма неизменен, и сумма бонуса рассчитывается по определенному алгоритму, но реализация алгоритма является переменной, такой как реализация производительности S и A.
Состав паттерна стратегии:
- Класс стратегии, класс стратегии инкапсулирует конкретный алгоритм (метод расчета производительности) и отвечает за конкретный процесс расчета.
- Класс среды (Context), Context принимает запрос клиента, а затем делегирует запрос определенному классу политики.
- Мост, контекст хотят сохранить ссылку на объект политики
//策略类(S)
var performanceS = function () {}
//算法S内部具体实现
performanceS.prototype.calculate = function ( salary ) {
return salary * 4;
}
//策略类(A)
var performanceA = function () {}
//算法A内部具体实现
performanceA.prototype.calculate = function ( salary ) {
return salary * 3;
}
//策略类(B)
var performanceB = function () {}
//算法B内部具体实现
performanceB.prototype.calculate = function ( salary ) {
return salary * 2;
}
Затем определите класс среды, который относится к нашему бонусному классу Bonus.
var Bonus = function () {
this.salary = null; //原始工资
this.strategy = null; //绩效公司对应的策略对象
}
Bonus.prototype.setSalary = function (salary) {
this.salary = salary; //设置原始工资
};
Bonus.portotype.setStrategy = function (strategy) {
this.strategy = strategy; //设置员工绩效等级对应的策略对象
}
Bonus.prototype.getBonus = function () { //取得奖金数额
//维持对策略对象的引用
return this.strategy.calculate( this.salary ); //委托给对应的策略对象
}
Призывы к бонусному классу
var bonus = new Bonus();
bonus.setSalary( 10000 );
bonus.setStrategy( new performanceS() ); //设置策略对象
console.log( bonus.getBonus() ); //把请求委托给了之前保存好的策略对象
В приведенном выше примере показано применение шаблона стратегии, который делает код более понятным и обязанности каждого класса более четкими, а также устраняет недостатки вышеописанных обычных вызовов функций и недостатков. Тогда реализация этого класса фактически основана на имитации традиционного объектно-ориентированного языка, поэтому мы можем дополнительно оптимизировать этот код и превратить его в JavaScript-версию шаблона стратегии.
Оптимизация 3 (режим стратегии версии JavaScript)
Почему режим стратегии версии JavaScript отличается от режима стратегии традиционного объектно-ориентированного языка?На самом деле, в языке JavaScript функции также являются объектами, поэтому класс стратегии может быть непосредственно определен как функция. код показывает, как показано ниже:
//策略对象
var strategies = {
//一系列算法
"S" : function ( salary ) {
return salary * 4;
},
"A" : function ( salary ) {
return salary * 3;
},
"B" : function ( salary ) {
return salary * 2;
}
};
Точно так же мы также можем напрямую использовать функцию calculateBonus в качестве контекста для приема пользовательских запросов без необходимости представления класса Bonus.
var calculateBonus = function ( level, salary) {
return strategies[ level ]( salary );
}
console.log( calculateBonus('S',20000));
Из приведенных выше примеров мы фактически ответили на три вопроса качества в определении шаблона стратегии: к чему относится алгоритм шаблона стратегии (метод расчета производительности), почему он инкапсулирован (многоразовый) и что значит заменять друг друга? (производительность может меняться, но на вызов функции это не влияет, просто меняются параметры)
расширение
Выше было определено понятие алгоритма в режиме стратегии.В реальной разработке мы обычно можем расширить значение алгоритма, так что режим стратегии также можно использовать для инкапсуляции ряда «бизнес-правил».Пока эти бизнес-правила указывают на одну и ту же цель и могут использоваться взаимозаменяемо., мы можем использовать шаблон стратегии, чтобы инкапсулировать их Затем в наших «бизнес-правилах» проверка формы соответствует нашему использованию шаблона стратегии.
проверка формы
Предположим, мы пишем страницу регистрации.Перед нажатием кнопки существуют следующие правила проверки:
- Имя пользователя не может быть пустым
- Длина пароля не может быть меньше 6 символов
- Номер мобильного телефона должен соответствовать формату
Согласно таким требованиям, прежде чем мы введем шаблон стратегии, мы можем написать следующий код
<html>
<body>
<form action='xxx.com' id='registerForm' method='post'>
请输入用户名:<input type='text' name='userName'/ >
请输入密码:<input type='text' name='password'/ >
请输入手机号码:<input type='text' name='phoneNumber'/ >
<button>提交</button>
</form>
<script>
var registerForm = document.getElementById('registerForm');
registerForm.onsubmit = function () {
if ( registerForm.userName.value === '') {
alert('用户名不能为空');
return false;
}
if (registerForm.password.value.length < 6) {
alert('密码长度不能小于6位');
return false;
}
if (!/(^1[3|5|8][0-9]{9}$/.test(registerForm.phoneNumber.value))) {
alert('手机号码格式不正确');
return false;
}
}
</script>
</body>
</html>
У такого кода тоже есть те же недостатки, что и у упомянутых выше наград конца года: слишком большие функции, недостаточная гибкость и плохая возможность повторного использования, значит, мы выучили шаблон стратегии и должны оптимизировать эту ситуацию.
оптимизировать один
- Понятно, что такое алгоритм в этом сценарии, очевидно, что алгоритм здесь относится к бизнес-правилам нашей логики проверки формы. Следовательно, мы можем инкапсулировать эти бизнес-правила в соответствующие объекты политики:
var strategies = {
isNonEmpty: function ( value, errorMsg) {
if ( value === '') {
return errorMsg;
}
},
minLength: function ( value, length, errorMsg ) {
if ( value.length < length ) {
return errorMsg
}
},
isMobile: function ( value, errorMsg) {
if ( !/(^1[3|5|8][0-9]{9}$)/.test( value )) {
return errorMsg;
}
]
}
Реализация алгоритма объекта политики, то нам также нужен класс окружающей среды для принятия запроса пользователя и доверить его к объекту стратегии. Но прежде чем мы реализуемся, нам нужно понять, как напрямую экологические и стратегические объекты, то естьКак пользователь отправляет запрос классу валидатора. Это облегчает нам реализацию класса среды, которым здесь является класс Validator. Вот код для нашего пользователя, чтобы отправить запрос классу валидатора:
var validataFunc = function () {
//创建一个validator对象
var validator = new Validator();
//添加校验规则
validator.add( registerForm.userName, 'isNonEmpty', '用户名不能为空');
validator.add( registerForm.password, 'minLength:6', '密码长度不能少于6位');
validator.add( registerForm.phoneNumber, 'isMobile', '手机号码格式不正确');
var errorMsg = validator.start();
//返回校验结果
return errorMsg;
}
var registerForm = document.getElementById('registerForm');
registerForm.onsubmit = function () {
var errorMsg = validataFunc(); //如果存在,则说明未通过校验
if ( errorMsg ) {
alert( errorMsg );
return false; //阻止表单提交
}
}
Из приведенного выше кода мы можем сделать понятно, что в нашем Валидаторе есть метод add, добавляем правила проверки через метод add, и в то же время есть метод запуска, который запускает нашу проверку через метод запуска, и возвращает сообщение об ошибке, если есть ошибка ( errorMsg) С объектом стратегии и мостом между объектом стратегии и классом среды (валидатором) мы можем написать наш код класса валидатора.
var validator = function () {
this.cache = []; //保存校验规则
};
//添加检验规则函数
validator.prototype.add = function (dom, rule, errorMsg) {
//把strategy和参数分开'minLength:6' 如'minLength:6' -> ["minLength", "6"]
var ary = rule.split(':');
this.cache.push ( function () {
var strategy = ary.shift(); //用户挑选的strategy ["minLength", "6"] -> 'minLength'
ary.unshift( dom.value ); //把input的value添加进参数列表
ary.push( errorMsg ); //把errorMsg添加进参数列表
return strategies[ strategy ].apply( dom, ary ); //委托策略对象调用
})
}
//检验开始函数
Validator.prototype.start = function () {
for ( var i = 0,validatorFunc; validatorFunc = this.cache[i++];) {
var msg = validatorFunc(); //开始校验,并取得校验后的返回信息
if ( msg ) { //如果msg存在,则说明校验不通过
return msg;
}
}
}
Выше мы завершаем проверку формы посредством абстракции алгоритма бизнес-правил и режима стратегии.При изменении правила проверки нам нужно изменить лишь небольшой объем кода. Если мы хотим изменить ввод имени пользователя не менее чем на 4 символа, нам нужно только изменить нашminLength:6изменить наminLength:4Только что
Оптимизация 2 (несколько правил проверки)
На самом деле, до сих пор наше понимание режима стратегии и основных концепций его применения объяснялось с помощью приведенных выше примеров, но в реализованной нами в настоящее время проверке формы есть небольшой изъян, то есть один из наших текстовых поля ввода соответствуют только одному типу проверки.проверьте правила. Затем, если мы хотим добавить несколько правил проверки, мы можем добавить их следующими способами:
validator.add( registerForm.userName, [{
strategy: 'isNonEmpty',
errorMsg: '用户名不能为空'
},{
strategy: 'minLength:10',
errorMsg: '用户名长度不能小于10位'
}])
Затем мы можем изменить метод добавления в нашем валидаторе и добавить наши несколько правил проверки в кеш путем обхода.
validator.prototype.add = function (dom, rules) {
var self = this;
for (var i = 0,rule; rule = rules[i++];) {
(function ( rule ) {
var strategyAry = rule.strategy.split( ':' );
var errorMsg = rule.errorMsg;
self.cache.push( function () {
var strategy = strategyAry.shift();
strategyAry.unshift( dom.value );
strategyAry.push( errorMsg );
return strategies[ strategy ].apply( dom, strategyAry )
})
})( rule )
}
};
Преимущества и недостатки паттерна стратегии
Из вышеприведенных примеров видно, что преимущества паттерна стратегии можно суммировать
- Используя методы и идеи, такие как композиция, делегирование и полиморфизм, эффективно избегая множественных операторов условного выбора.
- Принят принцип открытого-закрытого, а алгоритм заключен в независимую стратегию, которую легко понять, переключать и расширять.
- Алгоритмы в режиме стратегии можно использовать повторно, чтобы избежать копирования и вставки во многих местах.
В то же время, режим стратегии также имеет свои недостатки, но это не влияет на наше использование режима стратегии.
- В режиме стратегии мы добавим множество классов стратегий, объектов стратегии
- Чтобы использовать режим стратегии, мы должны понимать все стратегии, и мы должны понимать различия между каждой стратегией, чтобы выбрать подходящую стратегию.
Эпилог
После прочтения, если вы считаете, что что-то не так, пожалуйста, внесите предложения. Я также надеюсь, что если эта статья будет вам полезна, пожалуйста, поставьте лайк, перешлите и поддержите!
Статья заимствована из: Учителя Цзэн Таня»Шаблоны проектирования JavaScript и практика разработки》