Understand policy patterns in javascript

The definition of a strategy pattern is to define a series of algorithms, encapsulate them one by one, and make them interchangeable.

The advantages of using the policy pattern are as follows:

Advantages: 1. The strategy mode utilizes combination, delegation and other technologies and ideas, effectively avoiding many if conditional statements.

2. The policy pattern provides an open-closed principle that makes code easier to understand and expand.

3. Code in the policy pattern can be reused.

One: use the strategy mode to calculate the bonus;

The following demo is something I read in a book, but it doesn’t matter, we just want to understand the use of the strategy model, we can use the strategy model to calculate the bonus problem;

For example, the company’s annual bonus is assessed according to the salary and performance of employees. The annual bonus of A employee is 4 times the salary, 3 times the salary of B employee, and 2 times the salary of C employee. Now, using normal coding, we would write code like this:

var calculateBouns = function(salary,level) { if(level === 'A') { return salary * 4; } if(level === 'B') { return salary * 3; } if(level === 'C') { return salary * 2; }}; Console. log(calculateBouns(4000,'A')); // 16000 console.log(calculateBouns(2500,'B')); / / 7500Copy the code

The first parameter is salary, the second parameter is rank;

The code disadvantages are as follows:

  1. The calculateBouns function contains many if-else statements.
  2. The calculateBouns function is inelastic. If there is still grade D, we need to add an if statement to judge grade D in calculateBouns function.
  3. The algorithm is not reusable, if there are similar algorithms in other places, but the rules are not the same, our code can not be universal.

2. Use composition functions to refactor code

Combinatorial functions encapsulate algorithms into small functions, such as A small function for level A, A small function for level B, and so on; The following code:

var performanceA = function(salary) {
    return salary * 4;
};
var performanceB = function(salary) {
    return salary * 3;
};
        
var performanceC = function(salary) {
    return salary * 2
};
var calculateBouns = function(level,salary) {
    if(level === 'A') {
        return performanceA(salary);
    }
    if(level === 'B') {
        return performanceB(salary);
    }
    if(level === 'C') {
        return performanceC(salary);
    }
};
// 调用如下
console.log(calculateBouns('A',4500)); // 18000Copy the code

The code looks a bit better, but it still has the following shortcomings:

The calculateBouns function can get bigger and bigger, such as when increasing D levels, and is inelastic.

3. Refactor code using policy patterns

Strategy pattern refers to defining a series of algorithms, encapsulating them one by one, separating the invariable part from the changing part, in fact, separating the use and implementation of algorithms; The use of the algorithm is unchanged. It is based on a certain algorithm to obtain the calculated bonus number, and the implementation of the algorithm is based on the performance corresponding to different performance rules;

A program based on policy pattern consists of at least two parts. The first part is a set of policy classes, which encapsulate the specific algorithm and are responsible for the specific calculation process. The second part is the environment class Context, which receives client requests and then delegates them to a policy class. Let’s start with traditional object-oriented implementations;

The following code:

var performanceA = function(){}; performanceA.prototype.calculate = function(salary) { return salary * 4; }; var performanceB = function(){}; performanceB.prototype.calculate = function(salary) { return salary * 3; }; var performanceC = function(){}; performanceC.prototype.calculate = function(salary) { return salary * 2; }; Var Bouns = function(){this.salary = null; LevelObj = null; // Policy object corresponding to performance level}; Bouns.prototype.setSalary = function(salary) { this.salary = salary; // Save the original salary of the employee}; Bouns.prototype.setlevelObj = function(levelObj){ this.levelObj = levelObj; // Set the policy object corresponding to the employee performance level}; Return this.levelobj.calculate (this.salary); return this.levelobj.calculate (this.salary); return this.levelobj.calculate (this.salary); }; var bouns = new Bouns(); bouns.setSalary(10000); bouns.setlevelObj(new performanceA()); // Set the policy object console.log(bouns.getbouns ()); // 40000 bouns.setlevelObj(new performanceB()); // Set the policy object console.log(bouns.getbouns ()); / / 30000Copy the code

The code above was refactored using the policy pattern, and you can see that the code responsibilities are updated and the code becomes clearer.

4. Javascript version of the policy mode

The code is as follows:

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)); // 40000Copy the code

You can see that the code is much simpler;

Policy pattern refers to defining a series of algorithms and encapsulating them. However, policy pattern not only encapsulates algorithms, but also encapsulates a series of business rules. As long as these business rules have the same goal, we can use policy pattern to encapsulate them.

Form a charm

For example, we often conduct form verification, such as the registration and login dialog box, we need to perform verification operations before logging in: For example, there are the following logic:

  1. The user name cannot be empty
  2. The password must contain at least six characters.
  3. The mobile phone number must conform to the format.

For example, the HTML code looks like this:

<form action =" http://www.baidu.com" id="registerForm" method =" post"> <p> <label> Please enter user name: </label> <input type="text" name="userName"/> </p> <p> <label> please input password: </label> <input type="text" name="password"/> </p> <p> <label> Please input mobile phone number: </label> <input type="text" name="phoneNumber"/> </p> </form>Copy the code

Our normal form verification code is as follows:

var registerForm = document.getElementById("registerForm"); RegisterForm. Onsubmit = function () {if (registerForm. UserName. Value = = = ' ') {alert (' user name cannot be empty); return; } the if (registerForm. Password. The value. Length < 6) {alert (" the length of the password can't less than 6 "); return; } if(! / (a ^ 1 [3 | | 5 8] [0-9] {9} $) /. The test (value) registerForm. PhoneNumber.) {alert (" cell phone number format is not correct "); return; }}Copy the code

But writing code this way has the following disadvantages:

  1. The registerForm. onSubmit function is large and contains many if statements;
  2. The registerForm.onsubmit function is inelastic. If we add a new validation rule or want to change the password length from 6 to 8, we must change the code inside the registerForm.onsubmit function. It violates the open-closed principle.
  3. The reusability of the algorithm is poor. If another form is added to the program, and this form also needs to perform some similar effects, then we may need to copy the code again.

Next we can use the policy pattern to reconstruct form effectiveness;

The first step is to encapsulate the policy object. The following code:

var strategy = { isNotEmpty: function(value,errorMsg) { if(value === '') { return errorMsg; MinLength: function(value,length,errorMsg) {if(value. Length < length) {return errorMsg; }}, // mobileFormat: function(value,errorMsg) {if(! /(^1[3|5|8][0-9]{9}$)/.test(value)) { return errorMsg; }}};Copy the code

Next we are going to implement the Validator class, which acts as the Context and receives the user’s request and delegates it to the Strategy object, as follows:

var Validator = function(){ this.cache = []; // save the rule}; Validator.prototype.add = function(dom,rule,errorMsg) { var str = rule.split(":"); This.cache.push (function(){// STR returns minLength:6 var strategy = str.shift(); str.unshift(dom.value); // Add the input value to the argument list str.push(errorMsg); Return strategys[strategy]. Apply (dom, STR); }); }; Validator.prototype.start = function(){ for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) { var msg = validatorFunc(); If (MSG) {return MSG; }}};Copy the code

The Validator class acts as the Context here, receiving the user’s request and delegating it to the Strategys object. Add accepts three parameters, as shown in the following code:

Validator. add(registerform. password,’minLength:6′,’ password length must be at least 6 characters ‘);

Registerform. password is the dom node of the validation input box;

MinLength: 6: Is a string separated by a colon, minLength before the colon represents the Strategys object selected by the customer, and the number 6 after the colon represents the parameters that must be verified during the validation process. The value of the registerform. password text field should be at least 6 bits long; If the string does not contain a colon (:), no additional verification information is required.

The third parameter is the error message returned when the effect fails;

After we have added a set of validation rules to the Validator object, we call validator.start() to start the validation. If validator.start() returns an errorMsg string, the validation failed, and the registerForm.onSubmit method returns false to prevent the form submission. Let’s look at the initialization code as follows:

var validateFunc = function(){ var validator = new Validator(); Add (registerForm.userName,'isNotEmpty',' userName can't be empty '); Validator. add(registerform. password,'minLength:6',' password length must be at least 6 characters '); Validator.add (registerform. userName,'mobileFormat',' mobile number format is incorrect '); var errorMsg = validator.start(); Return errorMsg; }; var registerForm = document.getElementById("registerForm"); registerForm.onsubmit = function(){ var errorMsg = validateFunc(); if(errorMsg){ alert(errorMsg); return false; }}Copy the code

Here is all the code as follows:

var strategys = { isNotEmpty: function(value,errorMsg) { if(value === '') { return errorMsg; MinLength: function(value,length,errorMsg) {if(value. Length < length) {return errorMsg; }}, // mobileFormat: function(value,errorMsg) {if(! /(^1[3|5|8][0-9]{9}$)/.test(value)) { return errorMsg; }}}; var Validator = function(){ this.cache = []; // save the rule}; Validator.prototype.add = function(dom,rule,errorMsg) { var str = rule.split(":"); This.cache.push (function(){// STR returns minLength:6 var strategy = str.shift(); str.unshift(dom.value); // Add the input value to the argument list str.push(errorMsg); Return strategys[strategy]. Apply (dom, STR); }); }; Validator.prototype.start = function(){ for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) { var msg = validatorFunc(); If (MSG) {return MSG; }}}; var validateFunc = function(){ var validator = new Validator(); Add (registerForm.userName,'isNotEmpty',' userName can't be empty '); Validator. add(registerform. password,'minLength:6',' password length must be at least 6 characters '); Validator.add (registerform. userName,'mobileFormat',' mobile number format is incorrect '); var errorMsg = validator.start(); Return errorMsg; }; var registerForm = document.getElementById("registerForm"); registerForm.onsubmit = function(){ var errorMsg = validateFunc(); if(errorMsg){ alert(errorMsg); return false; }};Copy the code

We can see the benefits of using policy mode to write form validation code. We completed a form validation by adding configuration. This way, the code can be used as a component that can be called at any time, conveniently by passing parameters when changing form validation rules.

Adds multiple validation rules to a text input field

As we can see from the above code, we only give the input box corresponding to only one effect rule, for example, we can only verify whether the input box is empty,

Validator. add(registerform. userName,’isNotEmpty’,’ userName can’t be empty ‘); But if we want to verify that the input box is empty and that the length of the input box is at least 10 bits, we would expect to pass parameters like the following:

Add (registerForm.userName,[{strategy: ‘isNotEmpty’,errorMsg: ‘userName cannot be empty’}, {strategy: ‘minLength:6′,errorMsg:’ username length should not be less than 6 characters ‘}])

We can write code like this:

Var strategys = {isNotEmpty: function(value,errorMsg) {if(value === ") {return errorMsg; MinLength: function(value,length,errorMsg) {if(value. Length < length) {return errorMsg; }}, // mobileFormat: function(value,errorMsg) {if(! /(^1[3|5|8][0-9]{9}$)/.test(value)) { return errorMsg; }}}; var Validator = function(){ this.cache = []; // save the rule}; 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 strategys[strategy].apply(dom,strategyAry); }); })(rule); }}; Validator.prototype.start = function(){ for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) { var msg = validatorFunc(); If (MSG) {return MSG; }}}; Var registerForm = document.getelementById ("registerForm"); var validateFunc = function(){ var validator = new Validator(); / / create a Validator object / * add some efficacy rule * / Validator. Add (registerForm. UserName, [{strategy: 'isNotEmpty',errorMsg:' Username cannot be empty '}, {strategy: 'minLength:6',errorMsg:' username cannot be less than 6 characters '}]); Add (registerform. password,[{strategy: 'minLength:6',errorMsg:' Password length must be at least 6 characters '},]); The validator. Add (registerForm. PhoneNumber, [{strategy: 'mobileFormat, errorMsg:' phone number format is not correct '},]); var errorMsg = validator.start(); Return errorMsg; }; Onsubmit = function(){var errorMsg = validateFunc(); if(errorMsg){ alert(errorMsg); return false; }}Copy the code

Note: the above code is according to the book up to do, is to see the code book, the most important we understand the strategy pattern implementation, such as the above form validation function encapsulation code, so we usually use jquery plugin form validation code is so encapsulation, therefore we can also use this way to encapsulate the form such as learning;