Article series
A singleton of javascript design patterns
Adapter pattern for javascript design pattern
Decorator pattern for javascript design pattern
Javascript design pattern proxy pattern
Comparison of javascript adaptation, proxy, and decorator patterns
Javascript design pattern state pattern
Iterator pattern for javascript design pattern
Javascript design pattern strategy pattern
Javascript design pattern observer pattern
Publish subscriber pattern for javascript design pattern
concept
Strategy pattern: Define a set of algorithms with the same goal, encapsulate them one by one, and make them interchangeable.
In this paper, the code
Start with company performance
The year-end bonus of each company will be awarded according to the performance of employees in the year. Of course, Company A is no exception. Company A’s year-end performance system is as follows:
- Grade S, the year-end bonus is 4 times the salary
- For grade A, the annual bonus is three times of salary
- Grade B, the year-end bonus is twice the salary
- Grade C, the year-end bonus is 0.3 times the salary
The code implementation is as follows:
var calculateBonus = function (performanceLevel, salary) {
if (performanceLevel == 'S') {
return salary * 4
}
if (performanceLevel == 'A') {
return salary * 3
}
if (performanceLevel == 'B') {
return salary * 2
}
if (performanceLevel == 'C') {
return salary * 0.3}}console.info(calculateBonus('S'.20000)) / / 80000
console.info(calculateBonus('C'.15000)) / / 4500
Copy the code
Functionally, it can be used normally, but there are several disadvantages as follows:
- Violate the single function principle, in a function to deal with four performance logic, absolutely fat logic, this kind of irrefutable
- In violation of the open and closed principle, if you wanted to add a performance D logic, you would have to add a piece of logic to the calculateBonus function, which would require the tester to measure all the performance levels all over again.
- The algorithm has poor reusability. If this calculation rule is needed elsewhere, it can only be re-output (copy and paste).
Optimization one (combination function)
Let’s first solve the problem of poor reusability of the third algorithm. Encapsulate the various algorithms (i.e., the rules for calculating year-end bonuses) into small independent functions. Change the code to the following:
var performanceS = function (salary) {
return salary * 4
}
var performanceA = function (salary) {
return salary * 3
}
var performanceB = function (salary) {
return salary * 2
}
var performanceC = function (salary) {
return salary * 0.3
}
var calculateBonus = function (performanceLevel, salary) {
if (performanceLevel == 'S') {
return performanceS(salary)
}
if (performanceLevel == 'A') {
return performanceA(salary)
}
if (performanceLevel == 'B') {
return performanceB(salary)
}
if (performanceLevel == 'C') {
return performanceC(salary)
}
}
console.info(calculateBonus('S'.20000)) / / 80000
console.info(calculateBonus('C'.15000)) / / 4500
Copy the code
The reuse problem is solved by using the composite function, which separates each performance algorithm into a separate function. If there is another place to calculate s-level salary, call the performanceS function directly. However, the problems 1 and 2 above still exist, so we continue to optimize and introduce the protagonist strategy mode
Optimization 2 (Strategic Pattern)
An important part of design patterns is to separate the invariable parts from the changing parts, and the purpose of strategic patterns is to separate the use of algorithms from the implementation of algorithms.
In this example, the algorithm is used the same way, calculating the year-end bonus based on rank and salary; The implementation of the algorithm varies, for example, S has an S-level calculation method, and A has an A-level calculation method.
Composition of the strategy pattern:
- A group of policy classes that encapsulate specific algorithms and are responsible for specific calculation procedures.
- The Context class receives a request from a client and delegates the request to a policy class.
Define the policy class:
// Policy class (S)
class performanceS {
calculate(salary) {
return salary * 4; }}// Policy class (A)
class performanceA {
calculate(salary) {
return salary * 3; }}// Policy class (B)
class performanceB {
calculate(salary) {
return salary * 2; }}// Policy class (C)
class performanceC {
calculate(salary) {
return salary * 0.3; }}Copy the code
Define the environment class:
/ / environment
class Bonus {
constructor() {
this.salary = null; // Original salary
this.strategy = null; // Performance the corresponding strategic object of the company
}
setSalary(salary) {
this.salary = salary; // Set the original salary
}
setStrategy(strategy) {
this.strategy = strategy; // Set the policy object corresponding to the employee performance level
}
getBonus() {// Get the amount of bonus
// Maintain a reference to the policy object
// Delegate to corresponding policy objects
return this.strategy.calculate( this.salary ); }}Copy the code
Validation:
const bonus = new Bonus()
bonus.setSalary( 20000 );
bonus.setStrategy( new performanceS() ); // Set the policy object
console.info(bonus.getBonus()) / / 80000
Copy the code
The above shows the application of the strategy mode. The code is relatively clear and solves the above major problems. To add another performanceD logic, Bonus class will not be moved, just define a performanceD strategy class.
The policy pattern shown above is based on a traditional object-oriented language, and this code can be further optimized into a JavaScript version of the policy pattern.
Optimization three (JavaScript Versioning strategy mode)
In JS, functions are also objects, so you can define policy classes directly as functions and present them as object maps.
// Policy object
var strategies = {
// a series of algorithms
"S": function ( salary ) {
return salary * 4;
},
"A": function ( salary ) {
return salary * 3;
},
"B": function ( salary ) {
return salary * 2;
},
"C": function (salary) {
return salary * 0.3; }};Copy the code
Also, you can define the environment class as a function as follows:
var calculateBonus = function ( level, salary) {
return strategies[ level ]( salary );
};
Copy the code
Validation:
console.log( calculateBonus('S'.20000)); / / 80000
Copy the code
Let’s look at the definition of the policy pattern
Define a set of algorithms with the same goal, encapsulate them one by one, and make them interchangeable.
- Algorithm: Performance calculation method
- Encapsulation: The calculation method is encapsulated within the policy object to achieve reuse
- Substitution: When you want to change a performance, just change the parameters without affecting the function call
A more generalized algorithm.
Policy pattern refers to defining a series of algorithms and encapsulating them. However, policy pattern is not only used to encapsulate algorithms, but also can be used to encapsulate a series of business rules. As long as these business rules have the same goal, we can use policy pattern to encapsulate them. Like form validation
Form validation
Requirements:
- The user name cannot be empty
- The password must contain at least six characters
- The mobile phone number must conform to the format
Simple implementation
<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, Initial-scale =1.0"> <title> Form validation </title> </head> <body> <form action='xxx.com' id='registerForm' method='post'> Please enter user name: <input type='text' name='userName'/ > Please enter password: <input type='text' name='password'/ > Please enter mobile phone number: <input type='text' name='phoneNumber'/ > <button> Submit </button> </form> <script> var registerForm = document.getElementById('registerForm'); RegisterForm. Onsubmit = function () {if (registerForm. UserName. Value = = = ' ') {alert (' user name cannot be empty); return false; } the if (registerForm. Password. The value. Length < 6) {alert (' password length can not less than 6); return false; } if (! (/ ^ 1 [3 | | 5 8] [0-9] {9} $/. The test (value) registerForm. PhoneNumber.)) {alert (' cell phone number format is not correct); return false; } } </script> </body> </html>Copy the code
Similar to the above performance bonus, there are the same problems, such as excessively large function, lack of elasticity and poor reusability. The strategy mode optimization is adopted below
Optimization I (Strategic Pattern)
To use the policy pattern, first define the policy class. Then the policy class must first find out what the algorithm is: the business rules of the form validation logic
So define the policy class as follows:
// Define the policy class
const strategies = {
isNonEmpty: function ( value, errorMsg) {
if ( value === ' ') {
returnerrorMsg; }},minLength: function ( value, length, errorMsg ) {
if ( value.length < length ) {
return errorMsg
}
},
isMobile: function ( value, errorMsg) {
if(!/ (a ^ 1 [3 | | 5 8] [0-9] {9} $) /.test( value )) {
returnerrorMsg; }}}Copy the code
Next, we need to define the environment class. Before we define the environment class, let’s see how the customer usually uses it.
var validataFunc = function () {
// Create a Validator
var validator = new Validator();
// Add the verification rule
validator.add( registerForm.userName, 'isNonEmpty'.'User name cannot be empty');
validator.add( registerForm.password, 'minLength:6'.'Password length must not be less than 6 characters');
validator.add( registerForm.phoneNumber, 'isMobile'.'Incorrect format of mobile number');
var errorMsg = validator.start();
// Return the verification result
return errorMsg;
}
var registerForm = document.getElementById('registerForm');
registerForm.onsubmit = function () {
var errorMsg = validataFunc(); // If yes, the verification fails
if ( errorMsg ) {
alert( errorMsg );
return false; // Block form submission}}Copy the code
From the above code, it is clear that we need an Add method in the environment Validator to add validation rules.
If there is an error, return an error message (errorMsg).
With the policy object and the bridge between the policy object and the environment class (Validator), we can write the Validator class code
class Validator {
constructor() {
this.cache = []; // Save the verification rule
}
// Add the validation rule function
add(dom,rule,errorMsg){
'minLength:6' -> ["minLength", "6"]
let ary = rule.split(':');
this.cache.push ( function () {
// Select strategy ["minLength", "6"] -> 'minLength'
let strategy = ary.shift();
// Add input value to the parameter list
ary.unshift( dom.value );
// Add errorMsg to the parameter list
ary.push( errorMsg );
// Delegate policy object invocation
returnstrategies[ strategy ].apply( dom, ary ); })}start(){
for ( var i = 0,validatorFunc; validatorFunc = this.cache[i++];) {
var msg = validatorFunc(); // Start the verification and get the return information after the verification
if ( msg ) { // If MSG exists, the verification fails
returnmsg; }}}}Copy the code
In the above case, form validation is accomplished through the policy pattern by abstracting the algorithm of business rules. When changing a validation rule, we only need to change a small amount of code. For example, to change the user name to a maximum of four characters, change minLength:6 to minLength:4
Optimization 2 (Multiple validation rules)
There is a slight problem with the form validation currently implemented: only one validation rule can be used for each text input field. If you want to add multiple validation rules, you can add them in the following ways:
validator.add( registerForm.userName, [{
strategy: 'isNonEmpty'.errorMsg: 'User name cannot be empty'}, {strategy: 'minLength:10'.errorMsg: 'Username length cannot be less than 10 characters'
}])
Copy the code
To modify the Add method in the Validator, add multiple validation rules to the cache by traversing it.
add (dom, rules) {
let self = this;
for (let i = 0,rule; rule = rules[i++];) {(function ( rule ) {
let strategyAry = rule.strategy.split( ':' );
let errorMsg = rule.errorMsg;
self.cache.push( function () {
let strategy = strategyAry.shift();
strategyAry.unshift( dom.value );
strategyAry.push( errorMsg );
return strategies[ strategy ].apply( dom, strategyAry )
})
})(rule)
}
}
Copy the code
conclusion
Advantages:
- Policies are independent of each other, but they can be switched freely. The characteristics of the policy mode bring a lot of flexibility to the policy mode, and improve the reuse rate of policies.
- If the policy mode is not adopted, multiple conditional judgments are generally adopted when selecting a policy. The policy mode can avoid multiple conditional judgments and increase maintainability.
- Good scalability, the policy can be easily extended;
Disadvantages:
- In policy mode, we will add many policy classes, policy objects
- To use strategic patterns, we must understand all strategies and understand the differences between each strategy in order to choose a suitable strategy. For example, to choose a suitable travel route, we must first understand the details of choosing planes, trains, bicycles and other options. At this point strategy exposes all of its implementations to the customer, which violates the least knowledge principle.
Usage Scenarios:
- When multiple algorithms are only slightly different in behavior, the strategy mode can be used to dynamically select the algorithm.
- Scenarios where the algorithm needs to switch freely;
- Sometimes multiple conditional judgments are needed, so the policy pattern can be used to avoid the situation of multiple conditional judgments.
Refer to the link
JavaScript design patterns and development practices
conclusion
Your “like” is the biggest affirmation for me, if you feel helpful, please leave your appreciation, thank you!!