This is the 13th day of my participation in the August More Text Challenge

What is a strategic pattern

Define a set of algorithms, encapsulate them one by one, and make them interchangeable.

When I first looked at the definition, I thought it was a series of branching statements, such as:

function stragey(key){
switch(key){
    case 'a': return 1;
    case 'b':return 2; . } } stragey('b')
Copy the code

But if you want to add a new policy C, you go inside the function and add a new branch to the switch statement.

Use policy pattern refactoring

If we want to refactor a piece of code, we need to distinguish mutable parts from immutable parts. A policy pattern consists of at least two parts, one is the policy class, and the other is the environment class. We all know about the strategy class, which encapsulates specific algorithms and is responsible for specific calculation process. The environment class accepts a request from a client and delegates the request to a policy.

Let’s implement this from a class perspective:

/ / strategy
class strategyA{... }class strategyB{... }/ / environment
class Context{
    constructor(){
        this.obj=null
        this.strategy=null
    }
    setObj=(obj) = >{
        this.obj=obj
    }
    setStrategy=(strategy) = >{
        this.strategy=strategy
    }
    getResult=() = >{
        return  this.strategy.calculate(this.obj)
    }
}
Copy the code

Js version of the policy pattern instance

For example, the company will conduct performance statistics at the end of the year to generate year-end bonus. If the employee whose score is S will get 6 months ‘salary and be included in the list of best employees of the year, the employee whose score is A will get 4 months’ salary, the employee whose score is B will get 2 months ‘salary, the employee whose score is C will get 1 month’s salary, and the employee whose score is D will be fired directly.

For example, our list of employees:

const employeeList = [
  { name: "Zhu Once".level: "S".salary: 50 },
  { name: "Horse boy".level: "A".salary: 15 },
  { name: "Ma Xiaoling".level: "B".salary: 7 },
  { name: "Ma Xiaohao".level: "D".salary: 30},];Copy the code

As mentioned in the singleton pattern in the last article, writing a class in JS to generate a single object is like “fart off pants ———— superfluous action”. It is better to define an “object” directly, and different strategies are different methods in the object.

Year-end Bonus Strategy:

const FireList=[]

const strategy={
    'S':(employee) = >{
        pushToBestList(employee)
        return employee.salary*6
    }
    'A':(employee) = >{
        return employee.salary*4
    }
    'B':(employee) = >{
        return employee.salary*2
    }
    'C':(employee) = >{
        return employee.salary*1
    }
    'D':(employee) = >{
        pushToFireList(employee)
        return 0}}Copy the code

We need to give the accountant a year-end form, which can be calculated as follows:

const yearEndSalaryList=employeeList.map(employee= >{
    const {level}=employee
    returnThe strategy [level] (employee)})console.log(yearEndSalaryList) // Annual bonus summary
console.log(FireList) // Layoff list
Copy the code

Refactor your form in strategic mode

If you’ve written vanillaJS, you’re familiar with the following form verification code:

const form = document.getElementById("form");
form.onsubmit = () = > {
  if(! form.accont.value) { alert("Account name cannot be empty.");
    return;
  }

  if (form.password.value.length < 10) {
    alert("Password must be longer than 10 characters.");
    return; }};Copy the code

Here we take some of the form’s values directly and work with each of the attributes. This code is very common, but it’s not very reusable, and if you need multiple forms you have to copy them. Let’s use the strategy mode to sort it out:

The logic here is: if the checksum fails, an error with an error message is thrown.

const createError = (msg) = > {
  const err = new Error(a); err.msg = msg;return err;
};

const strategy = {
  required: (value, message) = > {
    if(! value || value.length ===0) {
      throwcreateError(message); }},minLength: (value, message, len) = > {
    if(! value || value.length < len) {throwcreateError(message); }}};Copy the code

With that done, the policy class in the policy pattern is ok, and the environment class is implemented. The environment class in the form that delegates form data to the policy class for processing. The logic here is to bind each delegate to the policy one by one and then execute it uniformly.

class Validator {
  constructor() {
    this.cache = [];
  }

  add(val, rule, msg) {
    const [strategyName, param = null] = rule.split(":");
    this.cache.push(function () {
      strategy[strategyName].apply(null, [val, msg, param]);
    });
  }

  validate() {
    this.cache.forEach((val) = >{ val(); }); }}const validate = (form) = > {
  const validator = new Validator();
  validator.add(form.account, "required"."Account name cannot be empty.");
  validator.add(form.pswd, "minLength:10"."Password must be longer than 10 characters.");

  try {
    validator.validate();
  } catch(err) { alert(err.msg); }};Copy the code
const mockForm = {
  account: "".pswd: "11"}; validate(mockForm);Copy the code

Of course, a form attribute may have multiple verification rules. We can verify multiple rules by adding policies, as shown below

const validate = (form) = > {
    const validator = new Validator();
    validator.add(form.account, "required"."Account name cannot be empty.");
    validator.add(form.account, "minLength:5"."Account length must be greater than 5 digits");
    validator.add(form.pswd, "minLength:10"."Password must be longer than 10 characters."); . };Copy the code

You can also verify rules in a similar way to antD forms:

const stockValidator = (_: any, value: string) = > {
  if (
    isNaN(Number(value)) || value.length ! = =6 ||
    (value[0]! = ="3" && value[0]! = ="6" && value[0]! = ="0"))return Promise.reject("Please enter the correct code");
    return Promise.resolve(); }; .const rules = [
  { required: true.message: "Enter stock symbol" },
  { validator: stockValidator }, ]; .Copy the code

conclusion

Policy pattern uses combination, delegation and polymorphism to avoid multi-conditional branching, which makes the code more reusable and is one of the best supports of open and closed principle. The disadvantage of the policy pattern, however, is that many policy classes are added to the program, and each policy in each policy class must be clearly distinguished from each other.

Recently I have been reading JS design Patterns and development practices. This article is my summary and expansion of this book. The next article will be about proxy patterns