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 if else logic written in the code, which will have a certain impact from the semantics and efficiency of the program. The most important thing 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('User name cannot be empty');
} else if (username.length < 5) {
    alert('Username must be at least 5');
} else if (username.length < 13) {
    alert('Username length must be less than 13');
} else if(! (/[a-z]+/i.test(username))) {
    alert('User name can contain only upper and lower case characters')}else {
    regeister(username);
}

// Verify password as above
Copy the code

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

4.2 Form validation based on policy mode

To change the idea, combined with the thought of policy mode, implement a special Validator class for value verification. The Validator is a scheduler, that is, the environment class in policy mode.

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 empty'
}
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."Content 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.'The length of the content cannot 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.'The length of the content cannot be greater than${length}`);
  },
  default: function () {
    return formatResult(true); }};/** * Validator * Policy pattern -- environment class, responsible for scheduling algorithm */
class Validator {
  // Store rules
  private _ruleExecuters: Array<any>;

  constructor() {
    this._ruleExecuters = [];
  }

  /** * addRules * add validation rules */
  public addRules(value: string = "", rules: Array<string>) {
    this._ruleExecuters = [];
    rules.forEach((rule) = > {
      const args = rule.split(":");
      const functionName = args.shift() || "default";
      // Ignore the 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) {returnres; }}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 validator’s capabilities by extending the validation algorithms in the policy class (object) ValidStrategies.

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 ' '; }}Copy the code

If you’re new to the program you might also have an if else condition to check the return value, and your code will be a little bit redundant.

With the help of the idea of table driven hair, here we can have optimization space, table driven hair written as follows:

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, in the design and implementation of program functions, if the use of strategic design mode, but also need our engineers to have a global function control ability, in order to better separation of dependence, abstraction, in order to highlight the “new generation” of migrant workers!