What is strategic mode

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



A policy-based program consists of at least two parts.

The first part is a set of policy classesstrategyThe policy class encapsulates the specific algorithm and is responsible for the specific calculation process.

The second section is the environment classContext ,  ContextAccept the client’s request and then delegate the request to a policy class

Second, the role of strategy mode

In reality, there are many ways to get to the same destination. For example, if we want to travel somewhere, we can choose the route according to the actual situation.

  1. If you don’t have the time but don’t care about money, you can fly.
  2. If you don’t have the money, you can take the bus or train.
  3. If you are poorer, you can choose to ride a bicycle.
Untitled Diagram.png

In programming, we often encounter a similar situation, to achieve a certain function there are many options. For example, a program to compress files can choose either zip algorithm or GZIP algorithm. These algorithms are flexible and interchangeable. This solution is the policy pattern described in this chapter.

3. Case of strategy mode

1. Calculate the bonus

Case Description: The year-end bonus of a company is based on the employee’s salary base and year-end performance. For example, the year-end bonus of S, A and B is 4 times the salary, 3 times the salary and 2 times the salary. The financial department requires us to provide A code to facilitate their calculation of employees’ year-end bonus.

Calculate bonus: Original version
const calculateBouns = function(level: string,salary: number) :number {

    if (level === 'S') {

      return salary * 4;

    }

    if (level === 'A') {

      return salary * 3;

    }

    if (level === 'B') {

      return salary * 2;

    }

 }

 console.log(calculateBouns('S'.4000));  / / output 16000

 console.log(calculateBouns('A'.3000));  / / output 9000

 console.log(calculateBouns('B'.2000));  / / output 4000

Copy the code

** Analysis ** :

  • calculateBonusIt’s a big function, it contains a lot of thingsif-elseStatements that need to override all logical branches.
  • calculateBonusThe function is inelastic. If we add a new performance level C, or want to change the bonus coefficient of performance S to 5, we have to go deepercalculateBonusInternal implementation of a function, which is a violationOpen – close principle.
  • Algorithms are not reusable. What if you need to reuse these algorithms for calculating bonuses elsewhere in the program? Our options are copy and paste.
Calculate the bonus :(using the policy pattern) object-oriented perfect version
// Calculate bonus: Object-oriented perfect version

class PerformanceS {

    calculate(salary: number) :number {

        return salary * 4

    }

}



class PerformanceA {

    calculate(salary: number) :number {

        return salary * 3

    }

}



class PerformanceB {

    calculate(salary: number) :number {

        return salary * 2

    }

}



interface strategy {

    calculate: (salary: number) = > number;

}

Copy the code

Create a Bonus (Context) object and set the Bonus object with some raw data, such as the original salary of the employee. Next, pass some policy object that calculates the bonus into the Bonus object and save it inside. When bonus.getBonus() is called to calculate the bonus, the Bonus object does not have the ability to do the calculation itself, but delegates the request to the previously saved policy object:

/ / the Context object

class Bouns {



    public salary: number// Original salary

    public strategy: strategy; // The policy object corresponding to the performance level



    setSalary(salary: number) {

        this.salary = salary; // Set the original salary of the employee

    }



    setStrategy(strategy: strategy) {

      this.strategy = strategy // Set the policy object corresponding to the employee performance level

    }



    getBouns() { // Get the amount of bonus

        return this.strategy.calculate(this.salary); // Delegate the calculation of bonuses to the corresponding policy object

    }

}



const bouns = new Bouns();



bouns.setSalary(4000);

bouns.setStrategy(new PerformanceS());

console.log(bouns.getBouns());  / / output 16000



bouns.setSalary(3000);

bouns.setStrategy(new PerformanceA());

console.log(bouns.getBouns());  / / output 9000



bouns.setSalary(2000);

bouns.setStrategy(new PerformanceB());

console.log(bouns.getBouns());  / / output 4000

Copy the code

Let’s revisit the idea of the strategy pattern: define a series of algorithms, encapsulate them one by one, and make them interchangeable. Define a set of algorithms and encapsulate each of them into a policy class. The algorithm is encapsulated in methods within the policy class. When a client makes a request to the Context, the Context always delegates the request to one of these policy objects for calculation.

Calculate bonus: a polished version of JavaScript
// Calculate bonus: a polished version of JavaScript

// In JavaScript, functions are also objects, so it is simpler and more straightforward to define strategy directly as a function

interface strategy {

    S:(salary: number) = > number;

    A:(salary: number) = > number;

    B:(salary: number) = > number;

}



const strategy: strategy= {

    S: function(salary: number) :number {

      return salary * 4;

    },

    A: function(salary: number) :number {

      return salary * 3;

    },

    B: function(salary: number) :number {

      return salary * 2;

    }

}

// Context

  var calcluateBouns = function(level: string,salary: number) :number{

    return strategy[level](salary);

  }

  console.log(calcluateBouns('S'.4000));  / / output 16000

  console.log(calcluateBouns('A'.3000));  / / output 9000

  console.log(calcluateBouns('B'.2000));  / / output 4000

Copy the code

2. Form validation

  1. User name (null for verification)
  2. Password (no less than 6 characters)
  3. Mobile phone number (verify whether it is in mobile phone number format)
Form validation: Initial version
<html>

<body>       

<form action="http://xxx.com/register" id="registerForm" method="post">

Please enter user name:<input type="text" name="userName"/ >

Please enter your password:<input type="text" name="password"/ >  

Please enter your 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; 

} 

  if ( registerForm.password.value.length < 6) {

   alert ( 'Password length must not be less than 6 characters'); 

   return false;  }

  if(!/ (a ^ 1 [3 | | 5 8] [0-9] {9} $) /The (value) registerForm. PhoneNumber.) {

   alert ( 'Incorrect format of mobile number'); 

   return false; 

} 

} 

</script>          

</body>     

</html>



Copy the code

Analysis:

  • registerForm.onsubmitIt’s a big function, it contains a lot of thingsif-elseStatements that need to override all validation rules.
  • registerForm.onsubmitFunctions are inelastic, so if we add a new validation rule, or want to change the password length from 6 to 8, we have to drill downregisterForm.onsubmitInternal implementation of a function, which is a violationOpen – closed principle.
  • The algorithm is not reusable, and if we add another form to our program that requires similar validation, we are likely to copy the validation logic all over the place
Form validation: The policy pattern case
// Policy object

const strategies: Object = {

    isEmpty(value: string, errMsg: string) :string {

      if(value === ' ') {

        return errMsg

      }

    },

    minLength(value: string, length: number, errMsg: string) : string{

      if (value.length<length) {

        return errMsg

      }

    },

    isMobile(value: string,errMsg: string) :string {

      if(! (/^1[34578]\d{9}$/.test(value))) {

        return errMsg

      }

    }

}



// Context

class Validator {

  

    public cache: Array<Function>;



    constructor() {

        this.cache = []

    }



    add(value: string, rule: string, msg: string) {

        const params: Array<string> = rule.split(':');

        this.cache.push((a)= > {

            const strategy: string = params.shift();

            params.unshift(value);

            params.push(msg);

            return strategies[strategy].apply(null, params)

        })

    }



    check(): string {

        let value: Function;

        for (value of this.cache) {

            const msg = value();

            if (msg) {

                return msg

            }

        }

    }

}







var submitBtn = document.getElementById('submitBtn');

var registerForm = document.getElementById('registerForm');

var validateFunc = function({

    var validator = new Validator();

  // Add rules

    validator.add(registerForm.username.value,'isEmpty'.'User name cannot be empty');

    validator.add(registerForm.password.value,'minLength:6'.'Password length must not be less than 6 characters');

    validator.add(registerForm.phone.value,'isMobile'.'Mobile phone number format is not correct');

  

  // Check the result

    var errMsg = validator.check();

    return errMsg;

}

  submitBtn.onclick = function({

    var errMsg = validateFunc();

    if(errMsg) {

      console.log(errMsg);

      return false;

    } else {

      console.log('Form validation successful')

    }

  }

Copy the code

Fourthly, advantages and disadvantages of strategic mode

Advantages:

  1. Policy pattern can avoid multiple conditional selection statements by using composition, delegation and polymorphism.
  2. The policy pattern provides the rightOpen – closed principlePerfect support for encapsulating algorithms in separatestrategMake them easy to switch, easy to understand, and easy to extend.
  3. Algorithms in the policy pattern can also be reused elsewhere in the system, avoiding a lot of repetitive copy-and-paste work.
  4. Make use of composition and delegation in the policy patternContextHaving the ability to execute algorithms is also a lighter alternative to inheritance.

Disadvantages:

1. Using the policy pattern will add many policy classes or policy objects to the program, but it is actually better than stacking the logic they are responsible for in the Context. 2. In order to use the strategy pattern, it is necessary to understand all strategies and the differences between them, so as 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.