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 classesstrategy
The policy class encapsulates the specific algorithm and is responsible for the specific calculation process.
The second section is the environment classContext
, Context
Accept 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.
- If you don’t have the time but don’t care about money, you can fly.
- If you don’t have the money, you can take the bus or train.
- If you are poorer, you can choose to ride a bicycle.
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 ** :
calculateBonus
It’s a big function, it contains a lot of thingsif-else
Statements that need to override all logical branches.calculateBonus
The 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 deepercalculateBonus
Internal 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
- User name (null for verification)
- Password (no less than 6 characters)
- 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.onsubmit
It’s a big function, it contains a lot of thingsif-else
Statements that need to override all validation rules.registerForm.onsubmit
Functions 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.onsubmit
Internal 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:
- Policy pattern can avoid multiple conditional selection statements by using composition, delegation and polymorphism.
- The policy pattern provides the rightOpen – closed principlePerfect support for encapsulating algorithms in separate
strateg
Make them easy to switch, easy to understand, and easy to extend. - Algorithms in the policy pattern can also be reused elsewhere in the system, avoiding a lot of repetitive copy-and-paste work.
- Make use of composition and delegation in the policy pattern
Context
Having 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.