As the saying goes, it’s all about strategy. When we talk about strategy, we tend to think about the cost of each situation. The strategy can also be embodied in our code, rational use of the strategy pattern to reconstruct the logic of complex code, will make the project easier to maintain and expand.

“The new generation of migrant workers” has flooded wechat moments in recent days, with a screenshot like this:

There are about 30 in the codeif elseLogic, from the semantics of the program and program efficiency theory is a certain impact, the most important is that it may be laughed at by other “new generation of migrant workers”

An experienced worker would use a switch case or a policy mode to refactor code. What is a policy mode?

A, definitions,

Strategy: a course of action and mode of struggle formulated according to the development of the situation in order to achieve a certain strategic task. Strategy pattern: A behavior design pattern that allows you to define a series of algorithms and place each algorithm in a separate class so that the objects of the algorithm are interchangeable. In the common front-end game incentive interaction, it is often involved that different scores will show different dynamic effects, which is actually a conditional strategy.

Two, advantages and disadvantages

Advantages:

  1. Implementation and use of isolation algorithm
  2. Run-time switchable algorithms
  3. Replace inheritance with composition
  4. Easy to expand, in line with the open and closed principle, you can introduce new policies without modifying the context

Disadvantages:

  1. Expose all policy interfaces to distinguish policy differences
  2. If the logic is simple, using policy patterns increases code redundancy

Three, implementation,

A policy pattern is one that defines a set of example algorithms and encapsulates each of them. It is an important idea in design pattern to separate the invariant part from the changing part, while strategy pattern is to separate the implementation of algorithm and the implementation of algorithm to reduce the coupling degree. A program based on the policy pattern consists of at least two parts: a policy class and an environment class. The policy class encapsulates the specific algorithm and is responsible for the specific calculation process, which can be understood as “executor”. The Context class accepts the client’s request and delegates it to a policy class, understood as a “scheduler.”

Policy mode in form validation

In Web projects, common forms have functions related to forms such as registration, login and modification of user information. At the same time, we will do some conditional verification of front-end input box values when submitting forms. Since the user input in the input box is arbitrary, the verification rules are relatively complex. If we do not use design pattern, our code may write more if and else judgment logic, which is really not very good in terms of readability and maintainability. Let’s walk through the strategy design pattern from the form validation features common in front-end Web projects.

4.1 Preliminary form validation

A long, long time ago, my form validation might have read something like this:

var username = $('#nuserame').val(); var password = $('#password').val(); if (! Username) {alert(' username cannot be empty '); } else if (username. Length < 5) {alert(' username length must be greater than or equal to 5'); } else if (username. Length < 13) {alert(' username length < 13'); } else if (! (/[a-z]+/i.test(username))) {alert(' username only contains English characters ')} else {regeister(username); } // Verify password as aboveCopy the code

It’s a little hard to write, but it works!

4.2 Form validation based on policy mode

To change the idea, combine the thought of policy pattern, implement a special Validator class for value verification. The Validator is a scheduler, that is, the environment class in policy pattern. Then when we verify the target field value targetValue, the usage is roughly as follows:

Validator.addRules(targetValue, ['isNonEmpty', 'minLength:5', 'maxLength:12']).valid();
Copy the code

The verifier returns the result field and the MSG field:

Return {result: false, MSG: 'cannot be null'}Copy the code

2 the Validator

Based on the above requirements, the Validator implementation is as follows:

const formatResult = (isPass: boolean = false, errMsg: string = "") => { return { result: isPass, msg: errMsg, }; }; const ValidStrategies = { isNonEmpty: function (val: string = "") { if (! Val) return formatResult(false, "contents cannot be empty "); }, minLength: function (val: string = "", length: string | number = 0) { console.log(val, length); if (typeof length === "string") length = parseInt(length); If (val. Length < length) return formatResult(false, 'content length must not be less than ${length}'); }, maxLength: function (val: string = "", length: string | number = 0) { if (typeof length === "string") length = parseInt(length); If (val. Length > length) return formatResult(false, 'Length cannot be greater than ${length}'); }, default: function () { return formatResult(true); }}; Private _ruleExecuters: Array<any>; private _ruleExecuters: Array<any>; constructor() { this._ruleExecuters = []; } /** * addRules * addRules */ public addRules(value: string = "", rules: Array<string>) {this._ruleExecuters = []; rules.forEach((rule) => { const args = rule.split(":"); const functionName = args.shift() || "default"; / / ignore this assertion type 👀 const ruleFunc = ValidStrategies [functionName as "isNonEmpty" | "minLength" | "maxLength" | "default" ].bind(this, value); this._ruleExecuters.push({ func: ruleFunc, args, }); }); return this; } /** * valid */ public valid() { for (let i = 0; i < this._ruleExecuters.length; i++) { const res = this._ruleExecuters[i].func.apply( this, this._ruleExecuters[i].args ); if (res && ! res.result) { return res; } } return formatResult(true); } } export default new Validator();Copy the code
const res = Validator.addRules("123", [
    "isNonEmpty",
    "minLength:5",
    "maxLength:12",
  ]).valid();
  console.log("res:", res);
Copy the code

This allows us to call the Validator directly to validate the value when we validate the form value. At the same time, you can extend the policy class (object)ValidStrategiesTo extend the validator’s capabilities.

Five, table drive method

The strategic pattern saves logical judgment and reminds me of a concept I’ve seen before called “table driven”, or “table lookup”, as explained by Baidu.com:

Table-driven methods use tables for specific purposes. Programmers often talk about “table-driven” methods, but textbooks never mention what a “table-driven” method is. Table-driven methods are a way to look up information in a table without having to use a lot of logical statements (if or Case) to find it. In fact, any information can be selected by the table. In simple cases, logical statements tend to be simpler and more straightforward. But as the logical chain becomes more complex, tables become more attractive.

For example 🌰, suppose we want to get the current day of the week, the code might look like this:

function getDay() { const day = (new Date()).getDay(); Switch (day) {case 0: return 'Sunday '; Case 1: return 'Monday '; / /... Case 6: return 'Saturday '; default: return ''; }} "" Javascript for the first time may have an 'if else' conditional statement to determine the return value, the code will appear more redundant. Const days = [' Sunday ', 'Monday ',' Tuesday ', 'Wednesday ',' Thursday ', 'Friday ',' Saturday ']; const days = [' Sunday ', 'Monday ',' Tuesday ', 'Wednesday ',' Thursday ', 'Friday ',' Saturday '] function getDay2() { return days[(new Date()).getDay()]; }Copy the code

Of course, the above is only a very simple 🌰, we only need the main point in the coding process, if there are similar scenes involved, please use this way to code, experience more pleasant!

Six, summarized

The policy design pattern isolates the code, internal data, and dependencies of various algorithms from other code. Algorithms can be executed by different clients through a simple interface and switched at run time. Of course, when designing and implementing functions, if we need to use the strategic design mode, it is more necessary for our engineers to have the ability to control the overall function, so as to highlight the difference of the “new generation”!