preface

Last year, I first read the performance optimization nuggets pamphlet of Xiu Yan, and gained a lot.

After that, I bought this book of core principles and application practices of JavaScript design patterns, and just happened to have a small volume of free learning activities recently, so I hurried to sort out this note, and supplemented the rest of the design patterns that were not written in the booklet. In order to understand and deepen my impression, I combined the examples of JavaScript writing in the learning process.

It is more like a summary study note than an article.

Why study design patterns?

Before you learn, what are design patterns?

Design Pattern is a series of routines to solve specific problems. It is not a syntax specification, but a set of solutions to improve code reusability, maintainability, readability, robustness, and security.

It is a set of used, widely known, classified code design lessons.

There are recipes for cooking, there are walkthroughs for games, and in every field there are “ways” that we can achieve our goals well and quickly. In the programming world, the “repertoire” of programming is design patterns.

Learning it is also learning the routine of the programming world, on the upgrade of strange dozen equipment has a great help. In the fast-changing world of the front end, design patterns are also “once learned, for life” knowledge.

Principles of design patterns

Describe a recurring problem and the core of the solution to the problem. This way, you can use the solution again and again without having to rework.

One big rule:

  • Demeter’s Law: Also known as the least knowledge law, a software entity should interact with as few other entities as possible, each software unit should have the least knowledge of the other units, and be limited to those software units closely related to its own unit.

Five principles:

  • The single responsibility principle: A class should have only one cause for change, in short, a single function.
  • Open Closed principle: Open for extensions, closed for modifications.
  • Richter’s substitution rule: Where a base class appears, a subclass must appear.
  • Interface isolation principle: an excuse should be a role, should not do things dare to do, should do all. In short, reduce coupling and dependency.
  • The dependency rollover principle: Program for interfaces that rely on abstractions rather than concrete ones.

Common in JavaScript are the single feature and open closed principles.

High cohesion and low coupling

Design patterns help us make our code more reusable, extensible, maintainable, and flexible. Our ultimate goal with design patterns is to achieve high cohesion and low coupling in our code.

To give an example in real life, for example, in a company, each department usually performs its own duties and does not interfere with each other. When each department needs to communicate, special person in charge will be used for docking.

In software, too, a functional module focuses on one function, and a module preferably only implements one function. This is called cohesion.

The interaction between modules and systems is inevitable, but we should try to reduce the case that a single module cannot be used independently or transplanted due to the interaction. As much as possible, separate interfaces are provided for external operations, which is called low coupling

Packaging changes

In real development, code that doesn’t change basically doesn’t exist, so I try to minimize code changes.

The core of a design pattern is to observe the change and immutability in your entire logic and then separate the immutability to make the change flexible and the immutability stable.

Types of design patterns

Commonly used can be divided into three types of creation, structure, behavior, a total of 23 patterns.

Create a type

The factory pattern

This type of design pattern is the creation pattern, which provides the best way to create objects.

In factory mode, we create objects without exposing the creation logic to the client, and by using a common interface to point to the newly created objects. In JS, it is implemented with the help of constructors.

example

A class is going to do an entry system, entry a person, you have to write once.

let liMing = {
  name: "Li Ming".age: 20.sex: "Male"};Copy the code

If multiple entries are made, a class can be created.

class Student {
  constructor(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex; }}let zhangSan = new Student("Zhang".19."Male");
Copy the code

The factory model is to separate the process of creating objects and use it so that only brainless input is needed, just like a factory, as long as enough raw materials are given, it can easily produce finished products.

summary

  • The constructor is separated from the creator, encapsulating the new operation
  • In line with the principle of openness and closure

The singleton pattern

Definition of a singleton pattern: guarantees that a class has only one instance and provides a global variable to access it.

This ensures that a class has only one instance object.

For example, Vuex and jQuery

example

Use scenario: a single object, such as a popover, should only be created once, no matter how many times it is clicked. Implementation is also simple, using a variable cache.

[Click to view Demo] : Singleton mode – online examples

For example, the above bullet frame will be created only when the button is clicked for the first time. After that, the bullet frame will not be created, but will be used before.

In this way, a bullet box applied to the singleton pattern is implemented.

summary

  • Maintains an instance and returns it if it has been created
  • In line with the principle of openness and closure

The prototype pattern

Specify what kind of objects to create with prototype instances, and create new objects by copying these prototypes.

example

In JavaScript, the prototype pattern is implemented in ECMAscript5 with the proposed object. create method, which uses existing objects to provide __proto__ for the created Object.

var prototype = {
  name: "Jack".getName: function() {
    return this.name; }};var obj = Object.create(prototype, {
  job: {
    value: "IT",}});console.log(obj.getName()); // Jack
console.log(obj.job); // IT
console.log(obj.__proto__ === prototype); //true
Copy the code

Where there is a prototype, there is a reason

Constructor pattern

In object-oriented programming languages, a constructor is a special method used in a class to initialize a new object. And can accept methods that parameters can be used to set the properties of the instance object

function Car(model, year, miles) {
  this.model = model;
  this.year = year;
  this.miles = miles;
  // this.info = new CarDetail(model)
  // Attributes can also be generated using new
}

// Override toString on the prototype object
Car.prototype.toString = function() {
  return this.model + " has done " + this.miles + " miles";
};

/ / use:
var civic = new Car("Honda Civic".2009.20000);
var mondeo = new Car("Ford Mondeo".2010.5000);
console.log(civic.toString()); // Honda Civic has done 20000 miles
console.log(mondeo.toString()); // Ford Mondeo has done 5000 miles
Copy the code

The constructor is implemented using inherited properties on the prototype chain.

Abstract Factory pattern

The Abstract Factory pattern uses class abstraction to make the business applicable to the creation of a cluster of product classes, not to instances of a particular class of products.

There is no direct abstract class in JS. Abstract is a reserved word, but not yet implemented, so we need to simulate the abstract class by throwing an error in the method of the class. If the method is not overridden in the inherited subclass, the error will be thrown.

const Car = function() {};
Car.prototype.getPrice = function() {
  return new Error("Abstract methods cannot be called");
};
Copy the code

In object-oriented languages, there is the abstract factory pattern. First, an abstract class is declared as a parent class to generalize the characteristics needed for a product class. The subclasses that inherit this parent class need to implement the methods declared in the parent class to implement the functions declared in the parent class:

/** * The subType class inherits from an abstract class of type superType in the factory class@param SubType The class * to be inherited@param The abstract class type */ in the superType factory class
const VehicleFactory = function(subType, superType) {
  if (typeof VehicleFactory[superType] === "function") {
    function F() {
      this.type = "Vehicle";
    }
    F.prototype = new VehicleFactory[superType]();
    subType.constructor = subType;
    subType.prototype = new F(); Subclass subType needs to inherit not only the stereotype methods of the corresponding superType class, but also its object properties
  } else throw new Error("This abstract class does not exist.");
};
VehicleFactory.Car = function() {
  this.type = "car";
};
VehicleFactory.Car.prototype = {
  getPrice: function() {
    return new Error("Abstract methods not available");
  },
  getSpeed: function() {
    return new Error("Abstract methods not available"); }};const BMW = function(price, speed) {
  this.price = price;
  this.speed = speed;
};
VehicleFactory(BMW, "Car"); // Inherits the Car abstract class
BMW.prototype.getPrice = function() {
  // Override the getPrice method
  console.log(`BWM price is The ${this.price}`);
};
BMW.prototype.getSpeed = function() {
  console.log(`BWM speed is The ${this.speed}`);
};
const baomai5 = new BMW(30.99);
baomai5.getPrice(); // BWM price is 30
baomai5 instanceof VehicleFactory.Car; // true
Copy the code

Abstract factories allow you to create products of a class cluster, and instanceof allows you to check the category of the product and has the necessary methods for that class cluster.

structured

Decorator mode

Decorator pattern, also known as decorator pattern. It is defined as “wrapping and extending the original object to meet the more complex needs of users without changing it”.

Decorator case

There’s a popover function that pops up when you click the button.

function openModal() {
  let div = document.craeteElement("div");
  div.id = "modal";
  div.innerHTML = "Tip";
  div.style.backgroundColor = "gray";
  document.body.appendChlid(div);
}
btn.onclick = () = > {
  openModal();
};
Copy the code

But suddenly the product manager had to change the requirement, changing the prompt text from “prompt” to “warning” and the background color from gray to red.

Do you immediately want to change the source function directly after hearing this:

function openModal() { let div = document.craeteElement("div"); div.id = "modal"; Div. InnerHTML = "Warning "; div.style.backgroundColor = "red"; document.body.appendChlid(div); }Copy the code

But if it’s complex business logic, or if this code is a legacy of previous code, it can be cumbersome to change it every time, considering future requirements.

Furthermore, directly modifying the existing function body violates our “open and closed principle”, and it violates the “single responsibility principle” to put so much logic into a function, so the above method is not optimal.

The best way to save time and effort is to ignore its existing logic and simply extend new functionality on top of it, hence the decorator pattern.

/ / the new logic
function changeModal() {
  let div = document.getElemnetById("modal");
  div.innerHTML = "Warning";
  div.style.backgroundColor = "red";
}
btn.onclick = () = > {
  openModal();
  changeModal();
};
Copy the code

This ability to add new functionality without modifying old logic through functions is the beauty of decorators.

Decorators in ES7

There is a decorator proposal in the latest ES7, but it is not finalized yet, so the syntax may not be final, but the idea is the same.

  1. Decorates the properties of the class
@tableColor
class Table {
  // ...
}
function tableColor(target) {
  target.color = "red";
}
Table.color; // true
Copy the code

Add a tableColor decorator to the Table class to change the color of the Table

  1. Methods that decorate classes
class Person {
  @readonly
  name() {
    return `The ${this.first} The ${this.last}`; }}Copy the code

Add a read-only decorator to the Name method of the Person class to make it unmodifiable.

This is done with the Wirteable feature of Object.defineProperty.

  1. Decorative function

    Since JS functions have function promotion, using decorators directly is not desirable, but can be implemented as advanced functions.

    function doSomething(name) {
      console.log("Hello, " + name);
    }
    function loggingDecorator(wrapped) {
      return function() {
        console.log("fun-Starting");
        const result = wrapped.apply(this.arguments);
        console.log("fun-Finished");
        return result;
      };
    }
    const wrapped = loggingDecorator(doSomething);
    let name = "World";
    
    doSomething(name); / / before decoration
    // output:
    // Hello, World
    
    wrapped(name); / / after decoration
    // output:
    // fun-Starting
    // Hello, World
    // fun-Finished
    Copy the code

    The decorator above prints a log at the start and end of a function.

reference

  • ES6 standard primer – Decorators
  • Recommended reading -core-decorators

Adapter mode

The role of the adapter pattern is to resolve interface incompatibilities between two software entities. Using the adapter pattern, two software entities that would otherwise not work due to incompatible interfaces can work together.

Simply put, it is to change the interface of a class into another interface that the client expects to solve the compatibility problem.

For example: axios

Example: a rendering map method, the default is to call the current map object show method for rendering operation, when there are multiple maps, and each map rendering method is not the same, in order to facilitate the user call, it needs to do adaptation.

let googleMap = {
  show: () = > {
    console.log("Start rendering Google Maps."); }};let baiduMap = {
  display: () = > {
    console.log("Start rendering Baidu Map"); }};let baiduMapAdapter = {
  show: () = > {
    returnbaiduMap.display(); }};function renderMap(obj) {
  obj.show();
}
renderMap(googleMap); // Start rendering Google Maps
renderMap(baiduMapAdapter); // Start rendering Baidu Map
Copy the code

Among them, “Baidu map” has been adapted to the processing.

summary

  • The adapter pattern solves the problem of mismatches between two interfaces. Instead of changing the existing interface, one object wraps another object
  • The adapter pattern conforms to the open closed principle
  • Keep the variation to yourself and the uniformity to the users.

The proxy pattern

Proxy mode – in some cases, due to various considerations/restrictions, one object cannot directly access another object, and a third party (proxy) is required to bridge the connection. This mode is called proxy mode.

Speaking of Proxy, I am familiar with the front end and can think of a series of things, such as:

  • Proxy attributes added in ES6
  • Proxy configurations for Webpack and Nginx proxies that are often used to solve cross-domain problems
  • There are also proxies for scientific Internet use.
  • , etc.

The event agent

When common lists and tables require separate events, using the parent element event broker can greatly reduce the amount of code.

<div id="father">
  <span id="1">Press 1</span>
  <span id="2">Press 2</span>
  <span id="3">Press 3</span>
  <span id="4">Press 4</span>
  <span id="5">Press 5</span>
  <span id="6">Press 6</span>
  <! - 7, 8... -->
</div>
Copy the code

In the code above, I want to click on each news item to get the ID of the current news item for the next step.

Binding an onclick event to each span would be too performance-intensive and cumbersome to write.

A common approach is to use the event bubbling principle to delegate the event to the parent element and then handle it all together.

let father = document.getElementById("father");
father.addEventListener("click".(evnet) = > {
  if (event.target.nodeName === "SPAN") {
    event.preventDefault();
    let id = event.target.id;
    console.log(id); // Get the id and proceed to the next step}});Copy the code

Virtual agent

For example, an expensive operation can be delayed until it is needed by a virtual proxy (for example, lazy image loading using a virtual proxy)

Image preloading: A loading map is used to store space, and then an image is loaded asynchronously. After the loading is complete, the original image is used to replace the loading map.

Ask what to use preloading + lazy loading? Take Taobao for example, there are so many pictures of goods in the mall. All the requests for so many pictures are a huge workload for the JS engine and the browser itself, which will slow down the browser response speed and poor user experience. The way of preloading + lazy loading will greatly save the browser request speed. The placeholder image is loaded first through preloading (the second time and later are read in the cache), and then lazy loading until the real image to load is complete, instant replacement. This mode is a good solution to the problem of poor user experience when images are displayed on the page.

Note: The first time the image is set to SRC, the browser sends a network request; If you set a requested SRC, the browser will read from the disk cache

class PreLoadImage {
  constructor(imgNode) {
    // Get the real DOM node
    this.imgNode = imgNode;
  }

  // Manipulate the SRC attribute of the img node
  setSrc(imgUrl) {
    this.imgNode.src = imgUrl; }}class ProxyImage {
  // Placeholder map URL
  static LOADING_URL = "xxxxxx";

  constructor(targetImage) {
    // Target Image, i.e. PreLoadImage instance
    this.targetImage = targetImage;
  }

  // This method mainly operates the virtual Image to complete the loading
  setSrc(targetUrl) {
    // The actual IMG node is initialized to display a placeholder map
    this.targetImage.setSrc(ProxyImage.LOADING_URL);
    // Create a virtual Image instance to help us load the Image
    const virtualImage = new Image();
    // When the target image is loaded, set the SRC property of the real IMG node in the DOM to the URL of the target image
    virtualImage.onload = () = > {
      this.targetImage.setSrc(targetUrl);
    };
    // Set the SRC attribute, and the virtual Image instance starts loading imagesvirtualImage.src = targetUrl; }}Copy the code

ProxyImage helps us schedule the work related to pre-loading. We can use the proxy ProxyImage to achieve indirect access to the real IMG node and get the effect we want.

In this case, the virtualImage object is a “hero behind the scenes” that always exists in the JavaScript world, initiating image loading requests in place of the real DOM, doing the image loading, and never showing up at the rendering level. This pattern is therefore called the “virtual proxy” pattern.

[Click to view Demo] : Virtual Proxy – online example

The caching proxy

Caching proxies are easier to understand and are used in some computationally intensive scenarios. In this scenario, we need to “trade space for time” — when we need to use a calculated value, we don’t want to have to do a second calculation, and we want to be able to pull the result out of memory.

In this scenario, we need a proxy to cache the results of the calculation while doing the calculation.

Example: Cache proxy for argument summation functions.

// The addAll method sums up all the arguments you pass in
const addAll = function() {
  console.log("A new calculation was made.");
  let result = 0;
  const len = arguments.length;
  for (let i = 0; i < len; i++) {
    result += arguments[i];
  }
  return result;
};

// Create a proxy for the summation method
const proxyAddAll = (function() {
  // Sum the result of the cache pool
  const resultCache = {};
  return function() {
    // Convert the input parameter to a unique input parameter string
    const args = Array.prototype.join.call(arguments.",");

    // Check whether the input parameter has the corresponding calculation result
    if (args in resultCache) {
      // If so, return the result already in the cache pool
      console.log("No computation - using cached data");
      return resultCache[args];
    }
    return(resultCache[args] = addAll(... arguments)); }; }) ();let sum1 = proxyAddAll(1.2.3); // A new calculation was performed

let sum2 = proxyAddAll(1.2.3); // No calculation - use cached data
Copy the code

The results are returned the first time the calculation is performed and cached. If the same argument is passed again, the result that exists in the cache is returned without calculation.

In common HTTP caching, the browser is equivalent to a layer of proxy caching, through the HTTP caching mechanism control (strong caching and negotiated caching) to determine whether to enable caching.

Frequent but small network requests, such as getUserInfo, can use proxy requests to set up uniform delivery and access.

summary

  • The proxy mode conforms to the open and closed principle.
  • Ontology objects and proxy objects have the same methods, and from the user’s point of view, it is not clear whether an ontology object or a proxy object is being requested.

The bridge model

Bridge pattern: Separate the abstract part from the concrete implementation part, which can change independently or work together.

In the implementation of this pattern, an object is required to act as a “bridge”, playing the role of the connection.

Example:

A typical use of the bridge pattern in JavaScript is the forEach function on Array objects.

This function is responsible for iterating through each element of a group. The callback function is the implementation part.

Below is the simulated forEach method:

const forEach = (arr, callback) = > {
  if (!Array.isArray(arr)) return;

  const length = arr.length;
  for (let i = 0; i < length; ++i) { callback(arr[i], i); }};// Here is the test code
let arr = ["a"."b"];
forEach(arr, (el, index) = > console.log("The element is", el, "位于", index));
// Element a is located at 0
// element b is located at 1
Copy the code

The appearance model

The Facade Pattern hides the complexity of the system and provides clients with an interface through which they can access the system. This type of design pattern is structural in that it adds an interface to an existing system to hide the complexity of the system.

This pattern involves a single class that provides simplified methods for client requests and delegate calls to existing system class methods.

example

Facade mode means that executing a method allows multiple methods to be called together.

When it comes to compatibility, parameters support multiple formats, environments, etc.. Expose a unified API

For example, self-encapsulated event objects contain compatible methods to prevent bubbling and add event listeners:

const myEvent = {
    stop (e){
        if(typeof e.preventDefault() == 'function'){
            e.preventDefault();
        }
        if(typeof e.stopPropagation() == 'function'){
            e.stopPropagation()
        }
        // IE
        if(typeOd e.retrunValue === 'boolean'){
            e.returnValue = false
        }
        if(typeOd e.cancelBubble === 'boolean'){
            e.returnValue = true}}addEvnet(dom, type, fn){
        if(dom.addEventListener){
            dom.addEventlistener(type, fn, false);
        }else if(dom.attachEvent){
            dom.attachEvent('on'+type, fn)
        }else{
            dom['on'+type] = fn
        }
    }
}
Copy the code

Portfolio model

Composite Pattern, also called partial whole Pattern, is used to treat a group of similar objects as a single object.

The composite pattern combines objects based on a tree structure, which is used to represent both part and whole hierarchies. This type of design pattern is a structural pattern that creates a tree structure of groups of objects.

This pattern creates a class that contains its own group of objects. This class provides a way to modify the same object group.

example

Imagine that we now have multiple universal remote controls in our hands. When we get home, we press a switch and the following will be done

  • Open the door
  • Open the computer
  • Open the music
// Prepare some functions to be executed in batches
class GoHome {
  init() {
    console.log("The door"); }}class OpenComputer {
  init() {
    console.log("Turn on the computer"); }}class OpenMusic {
  init() {
    console.log("Turn on the music"); }}// combinator, used to combine functions
class Comb {
  constructor() {
    // Prepare containers to prevent future combined functions
    this.skills = [];
  }
  // The function used for composition receives objects to be composed
  add(task) {
    // Fill the container with objects to be used in batches in the future
    this.skills.push(task);
  }
  // The function used for batch execution
  action() {
    // Get all the objects in the container
    this.skills.forEach((val) = >{ val.init(); }); }}// Create a combinator
let c = new Comb();

// Group objects in advance that will be operated on in batches
c.add(new GoHome()); // Add the 'open door' command
c.add(new OpenComputer()); // Add the 'start computer' command
c.add(new OpenMusic()); // Add the 'start music' command

c.action(); // Execute all the added commands
Copy the code

summary

  • A composite pattern is formed between objectsThe treestructure
  • Base objects and composite objects in composite modeBe treated as one
  • You don’t care how many layers the object has, you just call it inRoot call
  • Assemble and implement the functions of multiple objectsBatch execution

The flyweight pattern

The Flyweight Pattern is mainly used to reduce the number of objects created to reduce memory footprint and improve performance.

This type of design pattern is a structural pattern that provides a way to reduce the number of objects to improve the object structure required by the application.

The characteristics of

  • Shared memory (mainly memory, not efficiency)
  • The same data (memory), shared use

example

For example, the common event broker works by delegating the events of several child elements to a parent element, and the child elements share a method. If they are all bound to tags, the memory overhead is too high.

<! -- Click on span to get the current span -->
<div id="box">
  <span>1</span>
  <span>2</span>
  <span>3</span>
  <span>4</span>
</div>

<script>
  var box = document.getElementById("box");
  box.addEventListener("click".function(e) {
    let target = e.target;
    if (e.nodeName === "SPAN") { alert(target.innerHTML); }});</script>
Copy the code

summary

  • I’m abstracting out the same things
  • In line with the principle of openness and closure

Behavior type

Iterator pattern

The iterator pattern provides a way to access the elements of an aggregate object sequentially without exposing the internal representation of the object.

The iterator pattern separates the process of iterating from the business logic. After using the iterator pattern, each element of the object can be accessed sequentially, even if the internal structure of the object is not concerned.

The simple class says its purpose is to iterate over an traversable object.

Methods such as forEach and map in JS are a kind of implementation of the iterator pattern. Generally speaking, we do not need to implement the iterator ourselves.

There is a kind of array in JS, they do not have iterative methods, for example, nodeList, arguments can not directly use iterative methods, you need to use jQuery each method or assemble the class number into a real array to iterate.

In the latest VERSION of ES6, for can be used for any data type that has an Iterator interface. of.. Iterating, and his bottom layer is the next method repeatedly called, specific reference nguyen Yifeng -Iterator and for… Of circulation.

example

We can implement an Iterator ourselves using the Iterator interface.

class Creater {
  constructor(list) {
    this.list = list;
  }
  // Create an iterator, also known as a traversator
  createIterator() {
    return new Iterator(this); }}class Iterator {
  constructor(creater) {
    this.list = creater.list;
    this.index = 0;
  }
  // Determine whether the data is traversed
  isDone() {
    if (this.index >= this.list.length) {
      return true;
    }
    return false;
  }
  next() {
    return this.list[this.index++]; }}var arr = [1.2.3.4];
var creater = new Creater(arr);
var iterator = creater.createIterator();
console.log(iterator.list); // [1, 2, 3, 4]
while(! iterator.isDone()) {console.log(iterator.next());
  / / 1
  / / 2
  / / 3
  / / 4
}
Copy the code

summary

  1. Ordered data sets in JavaScript include Array, Map, Set, String, typeArray, arguments, and NodeList, excluding Object
  2. Any data with the [symbol. iterator] interface deployed can be used for… The of loop traverses
  3. The iterator pattern separates the target object from the iterator object, conforming to the open closed principle

Subscribe/Publish model (Observer)

The publish/subscribe pattern, also known as the observer pattern, defines a one-to-many dependency between objects. When an object’s state changes, all dependent objects are notified. In JavaScrtipt, we generally use a time model to replace the traditional publish/subscribe model.

Examples include bidirectional binding and event mechanisms in Vue.

The difference between publish/subscribe and observer

  • Publishers can receive subscriptions directly, called observer mode

  • Publishers do not directly touch subscribers, but a unified third party does the communication, called publish/subscribe

example

You can implement your own event bus that emulates $emit and $ON

class EventBus {
  constructor() {
    this.callbacks = {};
  }
  $on(name, fn) {
    (this.callbacks[name] || (this.callbacks[name] = [])).push(fn);
  }
  $emit(name, args) {
    let cbs = this.callbacks[name];
    if (cbs) {
      cbs.forEach((c) = > {
        c.call(this, args);
      });
    }
  }
  $off(name) {
    this.callbacks[name] = null; }}let event = new EventBus();
event.$on("event1".(arg) = > {
  console.log("event1", arg);
});

event.$on("event2".(arg) = > {
  console.log("event2", arg);
});

event.$emit("event1".1); // event1 1
event.$emit("event2".2); // event2 2
Copy the code

The strategy pattern

Define a set of algorithms, encapsulate them one by one, and make them replaceable.

The purpose of the policy pattern is to separate the use of algorithms from the implementation of algorithms.

A policy pattern usually consists of two parts:

  • A set of variable policy classes that encapsulate specific algorithms and are responsible for specific computation
  • A set of immutable environment classes: once a request is received, it is subsequently delegated to a policy class

Indicates that the environment class maintains a reference to a policy object.

example

Using performance ratings to calculate bonuses, you could easily write the following code:

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

Modify code using the policy pattern:

var strategies = {
  S: (salary) = > {
    return salary * 4;
  },
  A: (salary) = > {
    return salary * 3;
  },
  B: (salary) = > {
    return salary * 2; }};var calculateBonus = function(level, salary) {
  return strategies[level](salary);
};
console.log(calculateBonus("S".200)); // Output: 800
console.log(calculateBonus("A".200)); // Output: 600
Copy the code

The state pattern

The state mode allows an object to change when its internal state changes

The state pattern mainly deals with situations where the conditional expressions governing the state of an object are too complex. Complex decision logic can be simplified by moving the decision logic of states into a series of classes that represent different states.

example

Implement a traffic light switch.

Click to view Demo: Traffic Lights – online example

If you are adding a blu-ray, you can simply add a blu-ray class and add the parssBtn method without changing the other states.

summary

  • By defining different state classes, the behavior of state can be changed according to the change of state. It is not necessary to write a lot of logic in the class of the object being operated, and it is easy to add new states.
  • In line with the principle of openness and closure

Interpreter mode

** Given a language, define a representation of its grammar and define an Interpreter that uses that representation to interpret sentences in the language.

Use less, you can refer to two articles to understand.

  • Design mode – Interpreter mode – JavaScript
  • A detailed explanation of the javascript design pattern’s interpreter pattern

summary

  • How is the description language syntax defined, interpreted, and compiled
  • For professional scenarios

The mediator pattern

Mediator Pattern is used to reduce the complexity of communication between multiple objects and classes.

This pattern provides a mediation class that typically handles communication between different classes and supports loose coupling, making code easy to maintain

Through a mediator object, all other related objects communicate rather than refer to each other, but when one of them changes, the mediator object is only notified.

The mediator pattern can be used to decouple objects from previous objects.

For example: Vuex

Reference link: JavaScript mediator pattern

summary

  • Isolate the associated objects through a mediator
  • In line with the principle of openness and closure
  • To reduce the coupling

Visitor pattern

In the Visitor Pattern, we use a Visitor class that changes the execution algorithm of the element class.

In this way, the element’s execution algorithm can change as visitors change.

example

Invoke the methods of the element class through the visitor.

/ / visitors
function Visitor() {
  this.visit = function(concreteElement) {
    concreteElement.doSomething(); // use doSomething() for whoever accesses it.
  };
}
/ / element class
function ConceteElement() {
  this.doSomething = function() {
    console.log("This is a concrete element.");
  };
  this.accept = function(visitor) {
    visitor.visit(this);
  };
}
// Client
var ele = new ConceteElement();
var v = new Visitor();
ele.accept(v); // This is a concrete element
Copy the code

summary

  • If there are operations on an object that are irrelevant (or weakly related) to the object, you can use the visitor pattern to encapsulate those operations in the visitor to prevent them from contaminating the object.
  • If similar operations exist in a group of objects, they can be encapsulated in visitors to avoid a lot of repetitive code.

Memo mode

The Memento Pattern stores a certain state of an object so that it can be restored at an appropriate time

example

Implement an “editor” with the function of saving records, including

  • Record changes in the state of an object over time
  • A previous state can be restored at any time (such as undo)
// Status memo
class Memento {
  constructor(content) {
    this.content = content;
  }
  getContent() {
    return this.content; }}// Cheat sheet
class CareTaker {
  constructor() {
    this.list = [];
  }
  add(memento) {
    this.list.push(memento);
  }
  get(index) {
    return this.list[index]; }}/ / editor
class Editor {
  constructor() {
    this.content = null;
  }
  setContent(content) {
    this.content = content;
  }
  getContent() {
    return this.content;
  }
  saveContentToMemento() {
    return new Memento(this.content);
  }
  getContentFromMemento(memento) {
    this.content = memento.getContent(); }}// Test the code
let editor = new Editor();
let careTaker = new CareTaker();

editor.setContent("111");
editor.setContent("222");
careTaker.add(editor.saveContentToMemento()); // Store memos
editor.setContent("333");
careTaker.add(editor.saveContentToMemento()); // Store memos
editor.setContent("444");

console.log(editor.getContent()); / / 444
editor.getContentFromMemento(careTaker.get(1)); / / cancel
console.log(editor.getContent()); / / 333
editor.getContentFromMemento(careTaker.get(0)); / / cancel
console.log(editor.getContent()); / / 222
Copy the code

summary

  • Separation of state objects from consumers (decoupling)
  • In line with the principle of openness and closure

Template method pattern

In Template Pattern, an abstract class exposes a method/Template that executes its methods.

Its subclasses can override method implementations as needed, but the calls will be made in the manner defined in the abstract class.

Feel used not a lot, want to know can click on the reference link below.

Reference: JavaScript design pattern template method pattern

Chain of Responsibility model

As the name suggests, the Chain of Responsibility Pattern creates a Chain of recipient objects for a request.

This pattern decouples the sender and receiver of the request by giving the type of request.

In this pattern, each receiver typically contains a reference to the other receiver.

If an object cannot handle the request, it passes the same request to the next recipient, and so on.

example

Reimbursement approval process of the company: Group leader = project manager = Financial Director

// Leave approval requires the approval of the team leader, the manager and finally the director
class Action {
  constructor(name) {
    this.name = name;
    this.nextAction = null;
  }
  setNextAction(action) {
    this.nextAction = action;
  }
  handle() {
    console.log(`The ${this.name}The examination and approval `);
    if (this.nextAction ! =null) {
      this.nextAction.handle(); }}}let a1 = new Action("Team leader");
let a2 = new Action("Project Manager");
let a3 = new Action("Finance director");
a1.setNextAction(a2);
a2.setNextAction(a3);
a1.handle();
// Group leader approval
// Project manager approval
// Finance director approval

// Divide an operation into multiple responsibilities, one after the other, and finally complete the operation.
Copy the code

summary

  • Think of chain operations like jQuery and Promise
  • The initiator is isolated from the handler
  • In line with the principle of closed development

Command mode

Command Pattern is a data-driven design Pattern, which belongs to behavior Pattern.

The request is wrapped in the object as a command and passed to the calling object.

The calling object looks for a suitable object that can handle the command and passes the command to the corresponding object, which executes the command.

example

Implement an editor with many commands, such as: write, read, and so on.

class Editor {
  constructor() {
    this.content = "";
    this.operator = [];
  }
  write(content) {
    this.content += content;
  }
  read() {
    console.log(this.content);
  }
  space() {
    this.content += "";
  }
  readOperator() {
    console.log(this.operator);
  }
  run(. args) {
    this.operator.push(args[0]);
    this[args[0]].apply(this, args.slice(1));
    return this; }}const editor = new Editor();

editor
  .run("write"."hello")
  .run("space")
  .run("write"."zkk!")
  .run("read"); // => 'hello zkk! '

// Outputs the operation queue
editor.readOperator(); // ["write", "space", "write", "read"]
Copy the code

summary

  • To reduce the coupling
  • New commands can be easily added to the system

review

(1) Object-oriented final design objectives:

  • A Scalability: With new requirements, new capabilities can be easily added to the system without affecting existing performance or introducing new defects.

  • B Flexibility: Adding new functionality code changes happen smoothly without affecting other parts.

  • C replaceable: Some code in the system can be replaced with other classes of the same interface without affecting the system.

(2) Benefits of design mode:

  • A Design patterns make it easier to reuse successful designs and architectures.
  • B Design patterns also make it easier for developers of new systems to understand their design ideas.

(3) Learning design mode has three realms (seen many times online) :

  • # 1: You learn a design pattern while thinking about where it can be used in the project I just did (knife in hand, not knife in mind)

  • 2. You have learned all the design patterns, but when you come up against a problem, you find that there are several design patterns to choose from, and you have no choice (knife in hand, knife in mind).

  • Third and last, you may have no concept of design patterns, only a few design principles in mind that you can use when you need them (the highest level of knife skills: no knife in hand, no knife in mind).

conclusion

The following is a summary of the core principles and application practices of JavaScript design patterns, excerpted from the Gold Digger booklet.

This is the end of the design pattern journey. But for you, the real battle has just begun. The charm of design pattern is not on paper, but in practice.

Learn design patterns:

Read more – read source code, read materials, read good books;

Two, practice more – put what you have learned back into business development, see if it is OK, is there a problem? If there is a problem, how to fix it, how to optimize it? No design pattern is perfect. Design pattern, like human beings, is in the process of dynamic development. Not only the 23 design patterns proposed by GOF can be called design patterns.

As long as a solution follows design principles and solves a class of problems, it is entitled to the title of “design pattern”.

As you graduate from the design patterns booklet, I hope you will take with you not only knowledge, but also good study habits and reading habits. The most important is the courage to dig deep into theoretical knowledge and the determination to tackle technical problems. These things are not the so-called “koban” patent, but a good engineer must.

reference

  • Core principles and application practice of JavaScript design pattern
  • Common design patterns in JavaScript
  • Big talk about design patterns
  • Design Mode -W3CSchool
  • Design Mode – Novice tutorial

Original from Ninety: links to original blog posts

This article is part of the “Gold Nuggets For Free!” Event, click to view details of the event