This is the second day of my participation in the August More text Challenge. For details, see: August More Text Challenge

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

Calculate bonuses using the strategy model

Take the calculation of year-end bonus as an example, for example: the performance of S year-end bonus 4 times the salary, the performance of A year-end bonus 3 times the salary, the performance of B year-end bonus 2 times, write A piece of code to realize the calculation of employee year-end bonus.

1 Initial code implementation

var calculateBonus = function(performanceLevel, salary) {
  if (performanceLevel === 'S') {
    return salary * 4
  }
  if (performanceLevel === 'A') {
    return salary * 3
  }
  if (performanceLevel === 'B') {
    return salary * 2
  }
}
calculateBonus('B'.20000) / / output: 40000
calculateBonus( 'S'.6000 ); / / output: 24000
Copy the code

As you can see, this code is very simple, but it has a number of obvious drawbacks.

  • calculateBonusContains a lot ofif-elseStatement, need to override all logical branches.
  • calculateBonusInflexible, if you increase the performance C, or adjust the bonus factor, you have to go back in depthcalculateBonusFunction to modify, this is a violation of the open – close principle.
  • Algorithm reusability check, if needed elsewhere, only copy and paste.

Refactor the code using composite functions

All kinds of algorithms are encapsulated into a small function, these small functions have a good name, you can know which algorithm at a glance, they can also be reused.

var performanceS = function(salary) {
  return salary * 4
}
var performanceA = function(salary) {
  return salary * 3
}
var performanceB = function(salary) {
  return salary * 2
}
var calculateBonus = function(performanceLevel, salary) {
  if (performanceLevel === 'S') {
    return performanceS(salary)
  }
  if (performanceLevel === 'A') {
    return performanceA(salary)
  }
  if (performanceLevel === 'B') {
    return performanceB(salary)
  }
}
calculateBonus('A'.10000) / / output: 30000
Copy the code

Refactor the code using policy mode

The strategy pattern refers to defining a set of algorithms and encapsulating them one by one. Separating the unchanging from the changing is the theme of every design pattern, and the purpose of the policy pattern is to separate the use of an algorithm from its implementation.

A program based on the policy pattern consists of at least two parts. The first is a set of policy classes that encapsulate specific algorithms and are responsible for specific computing processes. The second part is the environment class Context, which accepts requests from customers and then delegates them to one of the policy classes.

The first version mimics the implementation in traditional object-oriented languages by encapsulating each performance calculation rule in a corresponding policy class:

var performanceS = function() {}
performanceS.prototype.calculate = function(salary) {
  return salary * 4
}
var performanceA = function() {}
performanceA.prototype.calculate = function(salary) {
  return salary * 3
}
var performanceB = function() {}
performanceB.prototype.calculate = function(salary) {
  return salary * 2
}
Copy the code

Next define Bonus:

var Bonus = function() {
  this.salary = null // The original salary
  this.strategy = null // The policy object corresponding to the performance level
}
Bonus.prototype.setSalary = function(salary) {
  this.salary = salary // Set the original salary of the employee
}
Bonus.prototype.setStrategy = function(strategy) {
  this.strategy = strategy // Set performance pay for employees
}
Bonus.prototype.getBouns = function() {
  return this.strategy.calculate(this.salary) // The operation of calculating bonuses is delegated to the corresponding policy object
}
Copy the code

The idea of the strategy pattern: define a set of algorithms, encapsulate them one by one, and make them interchangeable. It appears in JavaScript that they have the same goal and intent

Next, complete the rest of the code. Create a Bonus object and give it some raw data. Pass the policy object for calculating bonuses into the Bonus object internal store as well. When bonus. GetBonus () is called to calculate the bonus, the Bonus object does not have the ability to calculate by itself, instead delegating the request to a previously saved policy object.

var bonus = new Bonus()

bonus.setSalary(10000)
bonus.setStrategy(new performanceS()) // Set the policy object
console.log(bonus.getBonus()) / / output: 40000

bonus.setStrategy(new performanceA()) // Set the policy object
console.log(bonus.getBonus()) / / output: 30000
Copy the code

JavaScript version of policy mode

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

var strategies = {
  S: function(salary) {
    return salary * 4
  },
  A: function(salary) {
    return salary * 3
  },
  B: function(salary) {
    return salary * 2}}Copy the code

Again, Context doesn’t necessarily have to be a Bonus class, so define calculateBonus.

var calculateBonus = function(level, salary) {
  return strategies[level](salary)
}
console.log(calculateBonus('S'.20000)) / / output: 80000
console.log(calculateBonus('A'.10000)) / / output: 30000
Copy the code

Use the policy pattern to implement slow animation

Our goal is to get a ball moving according to a different algorithm.

The principle of achieving animation effects

The principle of using JavaScript to achieve the animation effect is the same as the production of animation, animation is some of the difference between the original painting to play a fast number of frames, to achieve visual animation effect. In JavaScript, you can animate an element by continuously adapting one of its CSS attributes, such as left, top, and background-position

Ideas and some preparation work

Before you start the exercise, you need to record some information, including at least:

  • The original position of the ball when the animation begins
  • The target position of the ball
  • The exact point in time when the animation starts
  • The duration of the ball movement

Then, use setInterval to create a timer that cycles every 19ms. In each frame of the timer, pass the elapsed time of the animation, the original position of the ball, the target position of the ball, and the total duration of the animation into the slow algorithm to calculate the current position of the ball. Then update the CSS properties corresponding to the div. Then the ball can move smoothly.

Get the ball moving

The easing algorithm accepts four parameters, meaning the elapsed time of the animation, the original position of the ball, the target position of the ball, and the total duration of the animation, and returns the value of the current position of the animation element.

var tween = {
  linear: function (t, b, c, d) {
    return (c * t) / d + b;
  },
  easeIn: function (t, b, c, d) {
    return c * (t /= d) * t + b;
  },
  strongEaseIn: function (t, b, c, d) {
    return c * (t /= d) * t * t * t * t + b;
  },
  strongEaseOut: function (t, b, c, d) {
    return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
  },
  sineaseIn: function (t, b, c, d) {
    return c * (t /= d) * t * t + b;
  },
  sineaseOut: function (t, b, c, d) {
    return c * ((t = t / d - 1) * t * t + 1) + b; }};Copy the code

The following code takes the idea from the jQuery library to place a div on the page:

<body>
  <div style="position: absolute; background: blue" id="div">I am a div</div>
</body>
Copy the code

Next we define the Animate class. The Animate constructor takes one parameter: the DOM node to be moved.

var Animate = function (dom) {
  this.dom = dom; // The DOM node that moves
  this.startTime = 0; // Start time of the animation
  this.startPos = 0; // The position of the DOM node when the animation starts, that is, the initial position of the DOM
  this.endPos = 0; // The position of the DOM node at the end of the animation, i.e. the target position of the DOM
  this.propertyName = null; // The name of the CSS property to be changed on the DOM node
  this.easing = null; // Slow algorithm
  this.duration = null; // Animation duration
};
Copy the code

Next, Animate. Prototype. start starts the animation, recording some information for later use when calculating the ball’s position. This method also starts the timer.

Animate.prototype.start = function (propertyName, endPos, duration, easing) {
  this.startTime = +new Date(a);// Animation start time
  this.startPos = this.dom.getBoundingClientRect()[propertyName]; // The initial position of the DOM node
  this.propertyName = propertyName; // The DOM node is the name of the CSS property that needs to be adapted
  this.endPos = endPos; // The target location of the DOM node
  this.duration = duration; // Animation duration
  this.easing = tween[easing]; // Slow algorithm

  var self = this;
  var timeId = setInterval(function () {
    // Start the timer to start the animation
    if (self.step() === false) {
      // If the animation has finished, clear the timer
      clearInterval(timeId); }},19);
};
Copy the code

The Animate. Prototype. start method takes the following four arguments:

  • propertyName: To changecssProperty name, for exampleleft,top, indicating left and right movement and up and down movement respectively.
  • endPos: The target position of the ball.
  • duration: Duration of the animation.
  • easing: Easing algorithm.

Animate. Prototype. step represents what to do for each frame of the ball’s motion.

Animate.prototype.step = function () {
  var t = +new Date(a);// Get the current time
  if (t >= this.startTime + this.duration) {
    // Fix the ball position at the end of the animation
    this.update(this.endPos); // Update the CSS property values of the ball
    return false;
  }
  var pos = this.easing(
    t - this.startTime,
    this.startPos,
    this.endPos - this.startPos,
    this.duration
  );
  // Pos is the current position of the ball
  this.update(pos); // Update the CSS property values of the ball
};
Copy the code

Animate. Prototype. Update to Animate.

Animate.prototype.update = function (pos) {
  this.dom.style[this.propertyName] = pos + "px";
};
Copy the code

Test it out:

var div = document.getElementById("div");
var animate = new Animate(div);
animate.start("left".500.1000."strongEaseOut");
Copy the code

More generalized “algorithm”

The strategy pattern refers to defining a set of algorithms and encapsulating them.

By definition, policy patterns are used to encapsulate algorithms. But using strategy patterns only to encapsulate algorithms is overkill. Practical development often spreads the meaning of algorithms so that policy patterns can also be used to encapsulate a set of “business rules.” As long as the business rules point to the same goal and can be used instead, we can use the policy pattern for encapsulation.

Advantages and disadvantages of the strategic pattern

Advantages:

  • The strategy pattern can effectively avoid multiple conditional selection statements by using techniques and ideas such as composition, delegation and polymorphism.
  • The strategy pattern provides perfect support for the open-close principle, encapsulating the algorithm in a separatestrategyTo 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 duplicated copy-and-paste work.
  • Use composition and delegation in the policy pattern to makeContextThe ability to execute algorithms is also a lighter alternative to inheritance.

Disadvantages:

  • Using the policy pattern adds many policy classes or policy objects to your program
  • All must be understood to use the policy patternstrategy, to understand eachstrategyBetween the differences in order to choose a suitable.

One last word

If this article is helpful to you, or inspired, please help to like and follow it, your support is the biggest motivation for me to keep writing, thank you for your support.